I have defined a custom element in JS. I wanna initialize it in its constructor for example append a template element as its child. but as you know, the browser throws an error that says:
Failed to construct 'CustomElement': The result must not have children
that means I can't append any child element to the custom element in its constructor.
I know that it can be solved if I create a shadow DOM, and then append elements to it, but I don't wanna use shadow DOM in every part of my app; and one another option is to append elements to the custom element in the connectedCallback
method, whereas it's invoked every time that the custom element mounts to the DOM, and I think it's not such cool actually. How can I fix that problem without using shadow DOM and connectedCallback
method? thanks
the custom component:
class ProductItem extends HTMLElement {
constructor() {
super()
this.appendChild(template.cloneNode(true))
}
}
customElements.define('product-item', ProductItem)
const template = document.createElement('article')
template.innerHTML = `
<a href="#">
<img />
<section>
<div>
<h4></h4>
<p class="price"></p>
<p></p>
</div>
<button>Add</button>
</section>
</a>
`
export { ProductItem }
You can not append in the constructor because the element is NOT in the DOM yet.
The same happens when you use .createElement("my-element")
So use the connectedCallback
Use isConnected
property:
https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected
And/Or your own boolean to check (in the connectedCallback) if your append has already run.
In Vanilla JS there is no other method which runs only once like Lit/Stencil/whatever tools have
Note: If you see code that does run; it is because the DOM was already parsed, and the constructor ran after DOM was parsed.
This usually happens when developers whack on defer
or async
on scripts that load (and define) the Web Component