相較於昨天的單選下拉選單,多選下拉選單組件要修改的只有以下幾個組件:下拉選單的內容組件,以及把所有組件串起來的組件。同時還要追加一個顯示個別資料用的組件。
還沒有處理optiongroup元素的部分
還沒有時間改成headless的無UI組件
HTML
<body>
<form id="form">
<my-multi-select>
<select multiple>
<option value="111" selected>111</option>
<option value="222" selected>222</option>
<option value="333">333</option>
<option value="444">444</option>
<option value="555">555</option>
<option value="666">666</option>
</select>
</my-multi-select>
<button type="submit">Submit</button>
</form>
<script type="module">
import { MyMultiSelect } from './select.js'
customElements.define("my-multi-select", MyMultiSelect);
const FormNode = document.getElementById("form");
FormNode.addEventListener("submit", (e) => {
e.preventDefault();
console.log(FormNode.elements[0].value);
});
</script>
</body>
因為需要顯示多重選擇。所以下拉選單的內容組件中需要加入一個checkbox,用來表示該選單是否有選擇。
const multiOptionStyle = `
// CSS的部分省略
`
class MyMultiSelectOption extends HTMLElement {
optionCheckbox = document.createElement('input');
constructor() {
super()
this.attachShadow({mode: 'open'})
const styleSheet = document.createElement('style')
styleSheet.innerHTML = multiOptionStyle
this.shadowRoot.appendChild(styleSheet)
this.shadowRoot.appendChild(this.render())
}
static get observedAttributes() {
return ['selected'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'selected') {
if (newValue !== null) {
this.optionCheckbox.setAttribute('checked', '');
this.optionCheckbox.checked = true;
} else {
this.optionCheckbox.removeAttribute('checked');
this.optionCheckbox.checked = false;
}
}
}
render() {
this.optionCheckbox.setAttribute('type', 'checkbox');
this.optionCheckbox.classList.add('checkbox');
const selected = this.hasAttribute('selected');
if (selected) {
this.optionCheckbox.setAttribute('checked', '');
this.optionCheckbox.checked = true;
this.optionCheckbox.setAttribute('value', this.getAttribute('value'));
}
const optioncontent = document.createElement('div');
optioncontent.classList.add('content');
optioncontent.innerHTML = `<slot></slot>`;
const optionContainer = document.createElement('label');
optionContainer.classList.add('container');
optionContainer.appendChild(this.optionCheckbox);
optionContainer.appendChild(optioncontent);
return optionContainer;
}
}
這個組件就只是用來 顯示個別資料
const multiChipStyle = `
// CSS的部分省略
`
class MyMultiSelectChip extends HTMLElement {
constructor() {
super()
this.attachShadow({mode: 'open'})
const styleSheet = document.createElement('style')
styleSheet.innerHTML = multiChipStyle
this.shadowRoot.appendChild(styleSheet)
this.shadowRoot.appendChild(this.render())
}
render() {
return document.createElement('slot')
}
}
因為有多選,會需要以下幾個功能
重點部分
class MyMultiSelect extends HTMLElement {
...
selectValueSet = new Set()
haslock = false
...
// 選擇項目的事件: 變成需要設定己選擇的項目,之後再來重新渲染資料
selectOption(option) {
return (e) => {
if (!this.haslock) {
this.haslock = true
const item = this.optionMap.get(option)
option.selected = true
item.selected = true
this.setValue(item)
this.renderSelectContent()
}
}
}
// 設定己選擇的項目的事件
setValue(item) {
if (this.selectValueSet.has(item)) {
this.selectValueSet.delete(item)
return false
} else {
this.selectValueSet.add(item)
return true
}
}
// 重新渲染資料用的事件
renderSelectContent() {
this.selectContentNode.innerHTML = ''
for (let item of this.selectValueSet) {
const chip = document.createElement('my-multi-select-chip')
chip.innerHTML = item.innerHTML
this.selectContentNode.appendChild(chip)
}
requestAnimationFrame(() => {
this.haslock = false
})
}
}