我们什么时候可以使用 javascript 访问自定义组件的 children 元素?
when can we access the children elements of a custom component using javascript?
所以我正在尝试使用 vanilla javascript 构建一个自定义组件,它将根据它拥有的 children 的数量来做某些事情,这意味着它必须计算所说的 [=31] =]
如果我有以下标记(其中自定义组件称为“my-component”)
<my-component>
<div></div>
<!-- ...arbitrary number of child elements -->
</my-component>
以及 <head></head>
中的以下 javascript 代码以确保它在 <body></body>
被解析之前加载
class MyComponent extends HTMLElement {
constructor(){
super()
this.children.length
//do stuff depending on the number of children
}
//or
connectedCallback () {
this.children.length
//do stuff depending on the numbre of children
}
}
customElements.define("my-component",MyComponent)
this.children.length
在这两种情况下都将 return 0,尽管元素随后显示在屏幕上,并且能够检查控制台上的自定义元素并获得 [=31 的预期数量=] 和 Element.children.length
。我想这意味着 children 元素在 constructor
和 connectedCallback
是 运行.
时尚不可用
有什么方法可以在我的元素的 class 定义中指定一个函数,该函数将在 children 元素可用时触发,以便我可以对它们进行处理?我希望有一个“childElementsReady”回调或类似的东西,但我猜它不存在。我不知道是否有一种非常明显的方法来处理我所缺少的,因为这似乎是我应该能够相对轻松地完成的事情
A MutationObserver 是处理此问题的最佳方法。您可以在 connectedCallback
中设置一个来观察光的变化 DOM - 在这种情况下,仅观察 childList
就足够了:
class MyElement extends HTMLElement {
constructor() {
super();
this.onMutation = this.onMutation.bind(this);
}
connectedCallback() {
// Set up observer
this.observer = new MutationObserver(this.onMutation);
// Watch the Light DOM for child node changes
this.observer.observe(this, {
childList: true
});
}
disconnectedCallback() {
// remove observer if element is no longer connected to DOM
this.observer.disconnect();
}
onMutation(mutations) {
const added = [];
// A `mutation` is passed for each new node
for (const mutation of mutations) {
// Could test for `mutation.type` here, but since we only have
// set up one observer type it will always be `childList`
added.push(...mutation.addedNodes);
}
console.log({
// filter out non element nodes (TextNodes etc.)
added: added.filter(el => el.nodeType === Node.ELEMENT_NODE),
});
}
}
customElements.define('my-element', MyElement);
每次将节点添加到 Light 时都会调用此处 onMutation
DOM,因此您可以在此处处理任何设置。
请注意,根据 Light DOM 中的节点,当元素连接到 DOM 时,onMutation
可能会被调用多次,所以不可能说所有 children 在任何时候都是 'ready' - 相反,您必须处理每个突变。
我写这篇回复是为了回答我自己的问题,因为我发现这是一种有用的方法,可以在您有阴影 dom 时观察添加的 children,所以希望它可以帮助任何人在那种情况下,但 lamplightdev 的回答是最完整的,因为它在您使用阴影 dom 或不使用阴影时都有效,因此也请查看他的回答
如果您的自定义元素使用阴影 dom,您可以这样做:
class MyComponent extends HTMLElement {
childAddedCustomCallback () {
let watchedSlot = this
/*"this" here is not the custom element, but a slot that the custom
element will have embedded in its shadow root, to which this function
will be attached as an event listener in the constructor*/
let children = watchedSlot.assignedElements()
let numberOfChildren = children.length
//do stuff depending on the number of children
}
constructor(){
super()
let shadowRoot = this.attachShadow({mode:"open"})
shadowRoot.innerHTML = "<slot></slot>"
let slotToWatch = shadowRoot.querySelector("slot")
slotToWatch.addEventListener("slotchange",this.childAddedCustomCallback)
}
}
customElements.define("my-component",MyComponent)
这样,每次在自定义元素上添加一个child,它都会反映到未命名的slot,slot的事件监听器会在发生时触发回调,给你一个可靠和清晰的在元素可用时立即访问 children 的方法
如果您将 n children 添加到自定义元素,这有执行 n 次的缺点,例如如果您有以下标记:
<my-component>
<div></div>
<div></div>
<div></div>
<div></div>
</my-component>
不是在添加最后一个 child 元素时执行一次,而是执行 4 次(每个 child 执行一次),所以要当心
所以我正在尝试使用 vanilla javascript 构建一个自定义组件,它将根据它拥有的 children 的数量来做某些事情,这意味着它必须计算所说的 [=31] =]
如果我有以下标记(其中自定义组件称为“my-component”)
<my-component>
<div></div>
<!-- ...arbitrary number of child elements -->
</my-component>
以及 <head></head>
中的以下 javascript 代码以确保它在 <body></body>
被解析之前加载
class MyComponent extends HTMLElement {
constructor(){
super()
this.children.length
//do stuff depending on the number of children
}
//or
connectedCallback () {
this.children.length
//do stuff depending on the numbre of children
}
}
customElements.define("my-component",MyComponent)
this.children.length
在这两种情况下都将 return 0,尽管元素随后显示在屏幕上,并且能够检查控制台上的自定义元素并获得 [=31 的预期数量=] 和 Element.children.length
。我想这意味着 children 元素在 constructor
和 connectedCallback
是 运行.
有什么方法可以在我的元素的 class 定义中指定一个函数,该函数将在 children 元素可用时触发,以便我可以对它们进行处理?我希望有一个“childElementsReady”回调或类似的东西,但我猜它不存在。我不知道是否有一种非常明显的方法来处理我所缺少的,因为这似乎是我应该能够相对轻松地完成的事情
A MutationObserver 是处理此问题的最佳方法。您可以在 connectedCallback
中设置一个来观察光的变化 DOM - 在这种情况下,仅观察 childList
就足够了:
class MyElement extends HTMLElement {
constructor() {
super();
this.onMutation = this.onMutation.bind(this);
}
connectedCallback() {
// Set up observer
this.observer = new MutationObserver(this.onMutation);
// Watch the Light DOM for child node changes
this.observer.observe(this, {
childList: true
});
}
disconnectedCallback() {
// remove observer if element is no longer connected to DOM
this.observer.disconnect();
}
onMutation(mutations) {
const added = [];
// A `mutation` is passed for each new node
for (const mutation of mutations) {
// Could test for `mutation.type` here, but since we only have
// set up one observer type it will always be `childList`
added.push(...mutation.addedNodes);
}
console.log({
// filter out non element nodes (TextNodes etc.)
added: added.filter(el => el.nodeType === Node.ELEMENT_NODE),
});
}
}
customElements.define('my-element', MyElement);
每次将节点添加到 Light 时都会调用此处 onMutation
DOM,因此您可以在此处处理任何设置。
请注意,根据 Light DOM 中的节点,当元素连接到 DOM 时,onMutation
可能会被调用多次,所以不可能说所有 children 在任何时候都是 'ready' - 相反,您必须处理每个突变。
我写这篇回复是为了回答我自己的问题,因为我发现这是一种有用的方法,可以在您有阴影 dom 时观察添加的 children,所以希望它可以帮助任何人在那种情况下,但 lamplightdev 的回答是最完整的,因为它在您使用阴影 dom 或不使用阴影时都有效,因此也请查看他的回答
如果您的自定义元素使用阴影 dom,您可以这样做:
class MyComponent extends HTMLElement {
childAddedCustomCallback () {
let watchedSlot = this
/*"this" here is not the custom element, but a slot that the custom
element will have embedded in its shadow root, to which this function
will be attached as an event listener in the constructor*/
let children = watchedSlot.assignedElements()
let numberOfChildren = children.length
//do stuff depending on the number of children
}
constructor(){
super()
let shadowRoot = this.attachShadow({mode:"open"})
shadowRoot.innerHTML = "<slot></slot>"
let slotToWatch = shadowRoot.querySelector("slot")
slotToWatch.addEventListener("slotchange",this.childAddedCustomCallback)
}
}
customElements.define("my-component",MyComponent)
这样,每次在自定义元素上添加一个child,它都会反映到未命名的slot,slot的事件监听器会在发生时触发回调,给你一个可靠和清晰的在元素可用时立即访问 children 的方法
如果您将 n children 添加到自定义元素,这有执行 n 次的缺点,例如如果您有以下标记:
<my-component>
<div></div>
<div></div>
<div></div>
<div></div>
</my-component>
不是在添加最后一个 child 元素时执行一次,而是执行 4 次(每个 child 执行一次),所以要当心