joicomponents
Version:
Design patterns to build native web components
95 lines (85 loc) • 2.36 kB
HTML
<script>
function excludeBranches(nodes, branchPoints) {
return Array.from(nodes).filter(function (node) {
for (let i = 0; i < branchPoints.length; i++) {
if (branchPoints[i].contains(node))
return false;
}
return true;
});
}
class MySelect extends HTMLElement {
constructor() {
super();
this.addEventListener("option-selected", this.optionSelected.bind(this));
}
optionSelected(e) {
e.stopPropagation();
const options = excludeBranches(this.querySelectorAll("my-option[selected]"), this.querySelectorAll("my-select"));
for (let i = 0; i < options.length; i++) {
let option = options[i];
option !== e.target && option.removeAttribute("selected");
}
}
}
class MyOption extends HTMLElement {
constructor() {
super();
this.addEventListener("click", this.clickSelect.bind(this));
}
static get observedAttributes() {
return ["selected"];
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === "selected" && newValue !== null && oldValue === null) {
this.dispatchEvent(new CustomEvent("option-selected", {bubbles: true}));
}
}
clickSelect(e) {
e.stopPropagation();
this.hasAttribute("selected") ? this.removeAttribute("selected") : this.setAttribute("selected", "");
}
}
customElements.define("my-select", MySelect);
customElements.define("my-option", MyOption);
</script>
<style>
my-select {
display: block;
border: 2px solid grey;
margin-left: 10px;
}
my-option {
display: block;
margin: 0 10px;
}
[selected] {
border-left: 4px solid red;
}
#inner [selected] {
border-left: 4px solid blue;
}
</style>
<my-select>
<my-option>click
<my-option>to
<my-option selected>select
<my-option>between</my-option>
<my-option>us</my-option>
</my-option>
</my-option>
</my-option>
<my-option>
Inner select
<my-select id="inner">
<my-option>click</my-option>
<my-option>to</my-option>
<my-option selected>select</my-option>
<my-option>between</my-option>
<my-option>us</my-option>
</my-select>
</my-option>
<my-option>so</my-option>
<my-option>many</my-option>
<my-option>options..</my-option>
</my-select>