如何在自定义元素 * 及其 children* 已初始化时获得回调
How to get a callback when a custom element *and it's children* have been initialized
我正在嵌套自定义元素。我想让我的 parent 自定义元素使用其 child 自定义元素原型中的方法和属性。例如
<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>
如内联所述,parent 无法读取 child 元素原型上定义的任何内容。不过,如果它立即触发 setTimeout,它就可以;只需要等到这个元素的child个节点也都设置好了即可。
出现这种情况是因为元素创建期间的回调顺序,我认为类似于:
- parent的原型已设置(从 HTMLElement 到 ParentElement)
- 调用了parent的createdCallback
- parent 的 attachedCallback 被调用
- child的原型已设置(从 HTMLElement 到 ChildElement)
- 调用了child的createdCallback
- child 的 attachedCallback 被调用
此时触发 createdCallback 是有道理的,但据我所知,当所有 children 都已创建时,根本没有可用的回调。我认为这意味着如果不使用 setTimeout 等待整个页面完成渲染,就不可能在使用 child 元素原型的创建过程中做任何事情。
您是否可以从 parent 监听任何回调或事件,以便仅在所有 child 节点的原型也已设置后触发?是否有不同的方法可以让您使用这样的结构?除了触发 setTimeout,您还有什么可以做的吗?
我认为自定义元素旨在允许与其他元素内容进行参数化,并且在该内容中有效地不支持自定义元素是非常令人惊讶的。
可以说这可以被认为是 Prototype not defined when accessing children on creation of custom-tag 的重复。然而,这个问题措辞不当,示例不完整,唯一的答案似乎实际上是一个实现错误,而不是解决方案(或者至少,它不再是当前的浏览器行为)
实际上,有许多不同的方法可以使用嵌套的自定义元素来实现这一点。
直接的方法是向父元素添加自定义回调,子元素将通过其 attachedCallback
方法调用该回调:
ParentElement.childAttachedCallback = function ()
{
console.log( this.childNodes[0].getName() )
}
ChildElement.attachedCallback = function()
{
this.parentElement.childAttachedCallback()
}
在复杂的情况下,您可以改用:
- 一个
Promise
对象由父对象设置并由一个或多个子对象解析,
- 由子项触发并由父项捕获的自定义事件,
- 一个
MutationObserver
对象设置为观察父子列表中的变化...
我正在嵌套自定义元素。我想让我的 parent 自定义元素使用其 child 自定义元素原型中的方法和属性。例如
<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>
如内联所述,parent 无法读取 child 元素原型上定义的任何内容。不过,如果它立即触发 setTimeout,它就可以;只需要等到这个元素的child个节点也都设置好了即可。
出现这种情况是因为元素创建期间的回调顺序,我认为类似于:
- parent的原型已设置(从 HTMLElement 到 ParentElement)
- 调用了parent的createdCallback
- parent 的 attachedCallback 被调用
- child的原型已设置(从 HTMLElement 到 ChildElement)
- 调用了child的createdCallback
- child 的 attachedCallback 被调用
此时触发 createdCallback 是有道理的,但据我所知,当所有 children 都已创建时,根本没有可用的回调。我认为这意味着如果不使用 setTimeout 等待整个页面完成渲染,就不可能在使用 child 元素原型的创建过程中做任何事情。
您是否可以从 parent 监听任何回调或事件,以便仅在所有 child 节点的原型也已设置后触发?是否有不同的方法可以让您使用这样的结构?除了触发 setTimeout,您还有什么可以做的吗?
我认为自定义元素旨在允许与其他元素内容进行参数化,并且在该内容中有效地不支持自定义元素是非常令人惊讶的。
可以说这可以被认为是 Prototype not defined when accessing children on creation of custom-tag 的重复。然而,这个问题措辞不当,示例不完整,唯一的答案似乎实际上是一个实现错误,而不是解决方案(或者至少,它不再是当前的浏览器行为)
实际上,有许多不同的方法可以使用嵌套的自定义元素来实现这一点。
直接的方法是向父元素添加自定义回调,子元素将通过其 attachedCallback
方法调用该回调:
ParentElement.childAttachedCallback = function ()
{
console.log( this.childNodes[0].getName() )
}
ChildElement.attachedCallback = function()
{
this.parentElement.childAttachedCallback()
}
在复杂的情况下,您可以改用:
- 一个
Promise
对象由父对象设置并由一个或多个子对象解析, - 由子项触发并由父项捕获的自定义事件,
- 一个
MutationObserver
对象设置为观察父子列表中的变化...