I'm trying to create a wrapper Custom Element that wraps its child Custom Elements into a div.
But the child elements aren't wrapped. Instead, an empty div is inserted into the wrapper element before the child elements
<script>
class ListItem extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.innerHTML = "<div>ListItem</div>";
}
}
class List extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.innerHTML = `<div class="list">${this.innerHTML}</div>`;
}
}
customElements.define("list-item", ListItem);
customElements.define("my-list", List);
</script>
<my-list>
<list-item></list-item>
<list-item></list-item>
<list-item></list-item>
</my-list>
This is the result:
<my-list>
<div class="list"></div>
<list-item><div>ListItem</div></list-item>
<list-item><div>ListItem</div></list-item>
<list-item><div>ListItem</div></list-item>
</my-list>
I would have expected the following:
<my-list>
<div class="list">
<list-item><div>ListItem</div></list-item>
<list-item><div>ListItem</div></list-item>
<list-item><div>ListItem</div></list-item>
</div>
</my-list>
You can try it out here.
It's due to the parsing execution sequence. When the <my-list>
tag is detected, it is created (and connected) immediately, before its children are inserted.
See issues addressed here WICG Web Components Issue #809 and here MDN connectedCallback
.
As a consequent ${this.innerHTML}
will return an empty string in connectedCallback()
.
You could wait for the children to be parsed, for example with the help of setTimeout()
:
class List extends HTMLElement {
connectedCallback() {
setTimeout( () =>
this.innerHTML = `<div class="list">${this.innerHTML}</div>`
)
}
}
But you'd better use Shadow DOM with <slot>
to insert elements of the light DOM:
class List extends HTMLElement {
connectedCallback() {
this.attachShadow( { mode: 'open' } )
.innerHTML = `<div class="list"><slot></slot></div>`
}
}
See the example below.
class ListItem extends HTMLElement {
connectedCallback() {
this.innerHTML = "<div>ListItem</div>";
}
}
class List extends HTMLElement {
connectedCallback() {
this.attachShadow( { mode: 'open' } )
.innerHTML = `<div class="list"><slot></slot></div>`
}
}
customElements.define("list-item", ListItem);
customElements.define("my-list", List);
<my-list>
<list-item></list-item>
<list-item></list-item>
<list-item></list-item>
</my-list>