Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get a callback when a custom element *and it's children* have been initialized

I'm nesting custom elements. I'd like to have my parent custom element use methods and properties from its child custom element's prototype. E.g.

<script>      
var ChildElement = Object.create(HTMLElement.prototype);

ChildElement.getName = function () {
  return "Bob";
}

document.registerElement("child-element", { 
  prototype: ChildElement
});

var ParentElement = Object.create(HTMLElement.prototype);

ParentElement.createdCallback = function () {
  var self = this;
  setTimeout(function () {
    // This logs 'Bob', correctly
    console.log(self.childNodes[0].getName());  
  }, 0);

  // This explodes - getName is not defined.
  console.log(self.childNodes[0].getName());
};

document.registerElement("parent-element", { 
  prototype: ParentElement
});
</script>

<parent-element><child-element></child-element></parent-element>

As noted inline, the parent cannot read anything that's defined on the child's element's prototype. It can if it fires an immediate setTimeout though; it just needs to wait until the child nodes of this element have also been set up.

It appears this happens because of the callback order during element creation, which I think is something like:

  • The parent's prototype is set (from HTMLElement to ParentElement)
  • The parent's createdCallback is called
  • The parent's attachedCallback is called
  • The child's prototype is set (from HTMLElement to ChildElement)
  • The child's createdCallback is called
  • The child's attachedCallback is called

That the createdCallback fires at this point makes sense, but as far as I can tell there's no callbacks available at all that fire when all your children have been created. I think that means it's impossible to ever do anything on creation that uses the prototype of your child elements, without using setTimeout to wait for the entire page to finish rendering.

Are there any callbacks or events you can listen to from the parent, to trigger only once the prototype of all your child nodes has also been set? Is there a different approach that would allow you to use structures like this? Is there anything else you can do, other than trigger a setTimeout?

I believed custom elements were designed to allow parameterization with other element content, and having custom elements effectively not supported in that content is quite surprising.

Arguably this could be considered a duplicate of Prototype not defined when accessing children on creation of custom-tag. However that question is poorly phrased, with broken examples, and the only answer appears to actually be an implementation bug, not a solution (or at least, it's no longer current browser behaviour)

like image 898
Tim Perry Avatar asked Mar 23 '16 19:03

Tim Perry


People also ask

How do I check if a custom element is defined?

The customElements. get() method can be used to check whether a given custom element has already been registered in the page. This can be used to find whether a given custom element name is available in the page or not, and prevent clashes with another custom element.

How do custom elements work internally?

The functionality of a custom element is defined using an ES2015 class which extends HTMLElement . Extending HTMLElement ensures the custom element inherits the entire DOM API and means any properties/methods that you add to the class become part of the element's DOM interface.


1 Answers

Actually, there are many different ways to achieve this with nested custom elements.

The direct way is to add a custom callback to the parent element that will be called by the child element from its attachedCallback method:

ParentElement.childAttachedCallback = function ()
{
    console.log( this.childNodes[0].getName() )
}

ChildElement.attachedCallback = function()
{
    this.parentElement.childAttachedCallback()
}

In a complex scenario you could use instead:

  • a Promise object set by the parent and resolve by one or more childs,
  • Custom Events fired by childs and caught by the parent,
  • a MutationObserver object set to observe changes in the parent's child list...
like image 95
Supersharp Avatar answered Oct 27 '22 00:10

Supersharp