Shadow DOM Slots - Composition
Web Components: Shadow DOM Slots - Composition
What is the HTML named <slot>
element used for in web development?
View Answer:
Why are Slots used in Web Components?
View Answer:
How many types of slots are there?
View Answer:
<template id="myComponent">
<div>
<slot name="header"></slot> <!-- named slot -->
<slot></slot> <!-- default slot -->
</div>
</template>
<script>
class MyComponent extends HTMLElement {
connectedCallback() {
const template = document.getElementById('myComponent');
const node = document.importNode(template.content, true);
this.attachShadow({mode: 'open'}).appendChild(node);
}
}
customElements.define('my-component', MyComponent);
</script>
<my-component>
<h1 slot="header">Hello World</h1> <!-- content for named slot -->
<p>This is some text.</p> <!-- content for default slot -->
</my-component>
In this example, we create a Web Component with a named slot (for a header) and a default slot. We then use the custom element, providing content for both slots.
What's the difference between a named slot and a default slot?
View Answer:
How can you select elements assigned to slots in Shadow DOM?
View Answer:
<template id="myComponent">
<div>
<slot name="header"></slot> <!-- named slot -->
<slot></slot> <!-- default slot -->
</div>
</template>
<script>
class MyComponent extends HTMLElement {
connectedCallback() {
const template = document.getElementById('myComponent');
const node = document.importNode(template.content, true);
this.attachShadow({mode: 'open'}).appendChild(node);
// After the node is attached
this.shadowRoot.addEventListener('slotchange', function(e) {
let slotElement = e.target;
let nodes = slotElement.assignedElements();
nodes.forEach(node => console.log(node.tagName)); // Outputs the tag names of the assigned elements
});
}
}
customElements.define('my-component', MyComponent);
</script>
<my-component>
<h1 slot="header">Hello World</h1> <!-- content for named slot -->
<p>This is some text.</p> <!-- content for default slot -->
</my-component>
In this example, after the Shadow DOM is attached, a 'slotchange' event listener is added. Whenever a slot change occurs, it selects the slot that changed (e.target
), gets all elements assigned to that slot with slotElement.assignedElements()
, and logs the tag name of each assigned element.
What's the difference between assignedNodes and assignedElements?
View Answer:
<template id="myComponent">
<div>
<slot name="header"></slot> <!-- named slot -->
</div>
</template>
<script>
class MyComponent extends HTMLElement {
connectedCallback() {
const template = document.getElementById('myComponent');
const node = document.importNode(template.content, true);
this.attachShadow({mode: 'open'}).appendChild(node);
// After the node is attached
this.shadowRoot.addEventListener('slotchange', function(e) {
let slotElement = e.target;
let nodes = slotElement.assignedNodes();
let elements = slotElement.assignedElements();
console.log('assignedNodes: ', nodes);
console.log('assignedElements: ', elements);
});
}
}
customElements.define('my-component', MyComponent);
</script>
<my-component>
<span slot="header">Hello </span>World <!-- content for named slot -->
</my-component>
In this example, slotElement.assignedNodes()
returns both the <span>
element and the following text node ("World"), while slotElement.assignedElements()
only returns the <span>
element.
Can you change the slot an element is assigned to?
View Answer:
<template id="myComponent">
<div>
<slot name="header"></slot> <!-- named slot -->
<slot name="footer"></slot> <!-- another named slot -->
</div>
</template>
<script>
class MyComponent extends HTMLElement {
connectedCallback() {
const template = document.getElementById('myComponent');
const node = document.importNode(template.content, true);
this.attachShadow({mode: 'open'}).appendChild(node);
}
}
customElements.define('my-component', MyComponent);
</script>
<my-component>
<h1 slot="header">Hello World</h1> <!-- content for named slot -->
</my-component>
<button onclick="changeSlot()">Change Slot</button>
<script>
function changeSlot() {
const h1 = document.querySelector('my-component h1');
h1.setAttribute('slot', 'footer'); // Changes the slot from "header" to "footer"
}
</script>
In this example, there's a <h1>
element initially assigned to the "header" slot. When the "Change Slot" button is clicked, the changeSlot()
function changes the slot
attribute of the <h1>
element from "header" to "footer", thereby changing which slot it is assigned to. After the function runs, the <h1>
element is assigned to the "footer" slot instead of the "header" slot.
What happens when multiple elements have the same slot name?
View Answer:
HTML (Light DOM):
<my-element>
<p slot="same-slot">First</p>
<p slot="same-slot">Second</p>
<p slot="same-slot">Third</p>
</my-element>
HTML (Shadow DOM):
<template id="my-element-template">
<div>
<slot name="same-slot"></slot>
</div>
</template>
<script>
class MyElement extends HTMLElement {
constructor() {
super();
let shadowRoot = this.attachShadow({mode: 'open'});
let template = document.getElementById('my-element-template');
shadowRoot.appendChild(template.content.cloneNode(true));
}
}
customElements.define('my-element', MyElement);
</script>
In this case, the shadow DOM has a single slot named same-slot
. The light DOM of the custom element my-element
has three <p>
elements, each also assigned to same-slot
. When the browser renders this, the content assigned to same-slot
in the light DOM gets inserted into the same-slot
slot of the shadow DOM. Since there are multiple elements assigned to same-slot
, they are inserted in order. So the resulting rendered HTML would look like:
<my-element>
<div>
<p slot="same-slot">First</p>
<p slot="same-slot">Second</p>
<p slot="same-slot">Third</p>
</div>
</my-element>
So the answer to your question is, when multiple elements have the same slot name, they are inserted into the slot in the order they appear in the light DOM.
Can a web component have more than one slot?
View Answer:
What happens to elements that don't have a slot attribute?
View Answer:
What happens if there is no default slot?
View Answer:
Is it necessary to use slots when creating custom elements?
View Answer:
What happens when a slot is deleted from the Shadow DOM?
View Answer:
Can a slotted element access data from the shadow host?
View Answer:
What is the definition of a flattened DOM?
View Answer:
What is the slot attribute's principal limitation?
View Answer:
<!-- invalid slot, must be direct child of user-card -->
<span slot="birthday">01.01.2001</span>
</div>
</user-card>
<!-- The slots below are appended in order -->
<user-card>
<span slot="username">John</span>
<span slot="username">Smith</span>
</user-card>
Can you explain what slot fallback content is in browser rendering?
View Answer:
In the Shadow DOM, what is the default slot?
View Answer:
<script>
customElements.define(
'user-card',
class extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
`;
}
}
);
</script>
<user-card>
<div>I like to swim.</div>
<span slot="username">John Smith</span>
<span slot="birthday">01.01.2001</span>
<div>...And play volleyball too!</div>
</user-card>
What are the three methods that handle HTML slot element assignment?
View Answer:
<custom-menu id="menu">
<span slot="title">Candy menu</span>
<li slot="item">Lollipop</li>
<li slot="item">Fruit Toast</li>
</custom-menu>
<script>
customElements.define(
'custom-menu',
class extends HTMLElement {
items = [];
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
`;
// triggers when slot content changes
this.shadowRoot.firstElementChild.addEventListener(
'slotchange',
(e) => {
let slot = e.target;
if (slot.name == 'item') {
this.items = slot
.assignedElements()
.map((elem) => elem.textContent);
console.log('Items: ' + this.items);
}
}
);
}
}
);
// items update after 1 second
setTimeout(() => {
menu.insertAdjacentHTML('beforeEnd', '<li slot="item">Cup Cake</li>');
}, 1000);
</script>