您面临的问题与我们团队在当前项目中遇到的问题基本相同:
connectedCallback
在 Chrome 中不保证孩子被解析。具体来说,依赖子级在升级情况下有效,但如果在浏览器解析元素时预先知道元素,则不起作用。因此,如果您将捆绑webcomponents.js
包放在body
.DOMContentLoaded
)。这基本上就是您发布的解决方案。
更糟糕的是,在自定义元素规范 v1 中没有保证子元素访问的生命周期钩子。
因此,如果您的自定义元素依赖于子节点进行设置(并且像您这样的简单 textNodetextContent
是子节点),这就是我们在经过一周的过度研究和测试后能够提取的内容(这也是 Google AMP 团队所做的) ):
class HTMLBaseElement extends HTMLElement {
constructor(...args) {
const self = super(...args)
self.parsed = false // guard to make it easy to do certain stuff only once
self.parentNodes = []
return self
}
setup() {
// collect the parentNodes
let el = this;
while (el.parentNode) {
el = el.parentNode
this.parentNodes.push(el)
}
// check if the parser has already passed the end tag of the component
// in which case this element, or one of its parents, should have a nextSibling
// if not (no whitespace at all between tags and no nextElementSiblings either)
// resort to DOMContentLoaded or load having triggered
if ([this, ...this.parentNodes].some(el=> el.nextSibling) || document.readyState !== 'loading') {
this.childrenAvailableCallback();
} else {
this.mutationObserver = new MutationObserver(() => {
if ([this, ...this.parentNodes].some(el=> el.nextSibling) || document.readyState !== 'loading') {
this.childrenAvailableCallback()
this.mutationObserver.disconnect()
}
});
this.mutationObserver.observe(this, {childList: true});
}
}
}
class MyComponent extends HTMLBaseElement {
constructor(...args) {
const self = super(...args)
return self
}
connectedCallback() {
// when connectedCallback has fired, call super.setup()
// which will determine when it is safe to call childrenAvailableCallback()
super.setup()
}
childrenAvailableCallback() {
// this is where you do your setup that relies on child access
console.log(this.innerHTML)
// when setup is done, make this information accessible to the element
this.parsed = true
// this is useful e.g. to only ever attach event listeners to child
// elements once using this as a guard
}
}
customElements.define('my-component', MyComponent)
<my-component>textNode here</my-component>
更新:很久以前 Andrea Giammarchi (@webreflection),自定义元素 polyfill 的作者document-register-element
(例如,Google AMP 正在使用它),他强烈主张将这种元素引入parsedCallback
自定义元素的 API,已经采取上面的代码并html-parsed-element
从中创建了一个包,这可能会对您有所帮助:
https://github.com/WebReflection/html-parsed-element
HTMLParsedElement
您只需从package 提供的基类(而不是)派生元素HTMLElement
。反过来,该基类继承自HTMLElement
.