Use slot elements in your ShadowDOM template.
Build your custom elements in a way so that they can live in any context, like as a child or parent element, without having any dependencies with other custom-elements. This approach will give you a modular design in which you can utilize your custom-elements in any situation.
But you still want to do something whenever a child element is present, like selecting them or calling a method on a child.
Slot elements
To tackle this the <slot>
element has been introduced. With slot elements you can create placeholders inside your ShadowDOM template. These placeholders can be used by simply placing an element inside your custom-element as a child in the DOM. The child element will then be placed inside the position where the <slot>
element is placed.
But how do you know if a placeholder has been filled with an element?
Slot elements can listen to a unique event called slotchange
. This will be fired whenever an element is (or multiple elements are) placed on the position of the slot
element.
Inside the listener of the event you can access all of the element in the placeholder with the HTMLSlotElement.assignedNodes()
or HTMLSlotElement.assignedElements()
methods. These return an array with the elements placed in the slot
.
Now you can wait for the children to be placed inside the slot and do something with the children that are present.
This way allows you to only manipulate the DOM and leave the ShadowDOM alone and let it do its work. Just like you would do with regular HTML elements.
Will the event wait for all child elements to be connected?
Yes, the slotchange
event is fired after all connectedCallback
methods of the custom elements have been called. This means no racing conditions or missing setup when listening to the event.
class ParentElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `
<h2>Parent Element</h2>
<slot></slot>
`;
console.log("I'm a parent and have slots.");
// Select the slot element from the ShadowDOM..
const slot = this.shadowRoot.querySelector('slot');
// ..and listen for the slotchange event.
slot.addEventListener('slotchange', (event) => {
// Get the elements assigned to the slot..
const children = event.target.assignedElements();
// ..loop over them and call their methods.
children.forEach(child => {
if (child.tagName.toLowerCase() === 'child-element') {
child.shout()
}
});
});
}
connectedCallback() {
console.log("I'm a parent and am now connected");
}
}
customElements.define('parent-element', ParentElement);
class ChildElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `
<h3>Child Element</h3>
`;
}
connectedCallback() {
console.log("I'm a child and am now connected.");
}
shout() {
console.log("I'm a child and placed inside a slot.");
}
}
customElements.define('child-element', ChildElement);
<parent-element>
<child-element></child-element>
<child-element></child-element>
<child-element></child-element>
</parent-element>
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…