'custom elements & connectedCallback() : wait for parent node to be available before firing a function
I'm using custom elements, which are very nice. But I'm facing a problem :
When the connectedCallback() function is called, it seems that the node is not yet at its place in the DOM, thus I cannot access its parents - and I need them.
class myElement extends HTMLElement{
constructor() {
super();
this.tracklist = undefined;
}
connectedCallback(){
this.render();
}
render(){
this.tracklist = this.closest('section');
// following code requires this.tracklist!
// ...
}
window.customElements.define('my-element', myElement);
How could I be sure the parent nodes are accessible before calling render() ?
Thanks !
Solution 1:[1]
It is a known issue:
connectedCallback does not mean your element is or is not fully parsed.
Custom Elements is lacking a parsedCallback method
See all the answers at:
- textContent empty in connectedCallback() of a custom HTMLElement connectedcallback-of-a-custom-htmlelement
- How to have a 'connectedCallback' for when all child custom elements have been connected
TL;DR;
The accepted method is to delay your render method:
connectedCallback(){
setTimeout(this.render);
}
Solution 2:[2]
It seems that the connectedCallback cannot access other elements in relation to itself when it hasn't been parsed yet. This kind of makes sense if you consider that a custom-element has to be able to live anywhere in the DOM without being dependent on another element. So if there were no parent to be selected, the element would probably not work properly.
A way to do this is to modify the render method to take an argument which will set the tracklist property dynamically to the custom element. Then select the my-element element from the DOM and look for the section.
Then use the customElements.whenDefined method to connect the section and my-element together whenever the custom element is ready. This method returns a Promise that resolves whenever the custom element is defined and gives you the ability to execute a callback.
See example below:
// Do something whenever the element is ready.
window.addEventListener('load', function() {
// Wait for the document to load so the DOM has been parsed.
window.customElements.whenDefined('my-element').then(() => {
const myElement = document.querySelector('my-element');
// Only do something if the element exists on the page.
if (myElement !== null) {
const tracklist = myElement.closest('section');
myElement.render(tracklist);
console.log(myElement.tracklist);
}
});
});
// Create element.
class myElement extends HTMLElement{
constructor() {
super();
this.tracklist = null;
}
render(tracklist){
this.tracklist = tracklist;
// following code requires this.tracklist!
// ...
}
}
// Define element.
window.customElements.define('my-element', myElement);
<section>
<my-element></my-element>
</section>
If I have been unclear or you have questions, please let me know.
Have a nice day!
Solution 3:[3]
I haven't tested this out but seems like a Promise might work:
// DomReady.js
class DomReady extends HTMLElement {
constructor() {
super();
this.domReadyPromise = new Promise(resolve => (this.domReadyResolve = resolve));
}
connectedCallback() {
this.domReadyResolve();
}
domReady() { return this.domReadyPromise; }
}
// ParentCustom.js
class ParentCustom extends DomReady {
connectedCallback() {
super.connectedCallback();
...
}
}
// ChildCustom.js
class ChildCustom extends HTMLElement {
async connectedCallback() {
await document.querySelector('parent-custom').domReady();
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 | underblob |
