原因将是connectedCallback()
在某些情况下,一旦浏览器满足以下条件,就会调用自定义元素开始标签自定义元素的,孩子们没有被解析,因此不可用。这确实例如如果您预先定义元素,然后浏览器解析 HTML,Chrome 中就会发生这种情况。
因此let inputs = this.getElementsByTagName('spk-input')
在你的update()
外层的方法<spk-root>
找不到任何元素。不要让自己被误导性的 console.log 输出所愚弄。
我最近刚刚深入研究了这个主题,并提出了一个使用HTMLBaseElement
class:
https://gist.github.com/franktopel/5d760330a936e32644660774ccba58a7 https://gist.github.com/franktopel/5d760330a936e32644660774ccba58a7
安德里亚·吉亚玛奇 (Andrea Giammarchi)document-register-element
polyfill 用于在不支持的浏览器中自定义元素)已采纳该解决方案建议并从中创建了一个 npm 包:
https://github.com/WebReflection/html-parsed-element https://github.com/WebReflection/html-parsed-element
只要您不需要动态创建自定义元素,最简单、最可靠的解决方法就是创建upgrade通过将元素定义脚本放在末尾的场景body
.
如果您对有关该主题的讨论感兴趣(长读!):
https://github.com/w3c/webcomponents/issues/551 https://github.com/w3c/webcomponents/issues/551
这是完整的要点:
HTMLBaseElement 类解决了在解析子级之前调用connectedCallback 的问题
Web 组件规范 v1 存在一个巨大的实际问题:
在某些情况下connectedCallback
当元素的子节点尚不可用时调用。
这使得 Web 组件在依赖其子组件进行设置的情况下无法正常工作。
See https://github.com/w3c/webcomponents/issues/551 https://github.com/w3c/webcomponents/issues/551以供参考。
为了解决这个问题,我们创建了一个HTMLBaseElement
我们团队中的类作为新类来扩展自主自定义元素。
HTMLBaseElement
依次继承自HTMLElement
(哪些自主定制元素必须源自其原型链中的某个点)。
HTMLBaseElement
添加两件事:
- a
setup
负责正确计时的方法(即确保子节点可访问),然后调用childrenAvailableCallback()
在组件实例上。
- a
parsed
布尔属性,默认为false
并且应该设置为true
当组件初始设置完成后。这是为了充当警卫以确保例如子事件侦听器永远不会附加多次。
HTML基本元素
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 once
// to child element nodes using this as a guard
}
}