How to get a callback when a custom element *and i

2019-02-13 19:15发布

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)

1条回答
We Are One
2楼-- · 2019-02-13 20:01

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...
查看更多
登录 后发表回答