Issue
Is there a way to wrap each individual element slotted into the shadow dom for a specific slot name?
Assume the Markup looks similar to this
<custom-element>
<div name="item">item 1</div>
<div name="item">item 2</div>
</custom-element>
Currently, the render is similar to:
<custom-element>
<div class="wrap">
<div name="item">item 1</div>
<div name="item">item 2</div>
</div>
</custom-element>
How would I go about wrapping the slotted elements to output similar to:
<custom-element>
<div class="wrap">
<div name="item">item 1</div>
</div>
<div class="wrap">
<div name="item">item 2</div>
</div>
</custom-element>
My current (flawed) approach:
customElements.define('custom-element', class MyCustomElement extends HTMLElement {
constructor(...args) {
super(...args);
let shadow = this.attachShadow({mode: open});
shadow.innerHTML = `
<div class="wrap">
<slot name="item"></slot>
</div>
`;
}
});
Solution
I would recommend that you change your approach as to the wrapping of the children. The simplest way would be to just add the missing HTML as a child of the slotted div like the example below. You would still be able to style the slotted element with the ::slotted
pseudo selector.
customElements.define('custom-element', class MyCustomElement extends HTMLElement {
constructor(...args) {
super(...args);
let shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
::slotted([slot="item"]) {
border: 1px solid black;
padding: 15px;
}
</style>
<slot name="item"></slot>
`;
}
});
<custom-element>
<div slot="item">
<div class="wrap">item 1</div>
</div>
<div slot="item">
<div class="wrap">item 2</div>
</div>
</custom-element>
The reason behind this approach is that the wrap ultimately belongs to the child element and should come with each child. The result would be similar to what you request.
Although, if you do want to add wrapping to the element dynamically, then you could do with the slotchange
event. The event is fired whenever a slot has been filled and can be listened to from the ShadowRoot
element. In the event callback loop over the assignedElements
(which are the elements in the slot) and alter their innerHTML
value.
customElements.define('custom-element', class MyCustomElement extends HTMLElement {
constructor(...args) {
super(...args);
let shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `<slot name="item"></slot>`;
shadow.addEventListener('slotchange', event => {
const { target } = event;
const assignedElements = target.assignedElements();
for (const element of assignedElements) {
if (element.querySelector('.wrap') === null) {
const wrapper = document.createElement('div');
wrapper.className = 'wrap';
for (const node of element.childNodes) {
wrapper.appendChild(node);
}
element.append(wrapper);
}
}
});
}
});
.wrap {
border: 1px solid black;
padding: 15px;
}
<custom-element>
<div slot="item">item 1</div>
<div slot="item">
<div class="wrap">item 2</div>
</div>
</custom-element>
Answered By - Emiel Zuurbier
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.