如何单独包装开槽元素
How to wrap slotted elements individually
有没有办法将插槽中的每个单独元素包装到特定插槽名称的影子 dom 中?
假设标记与此相似
<custom-element>
<div name="item">item 1</div>
<div name="item">item 2</div>
</custom-element>
目前,渲染类似于:
<custom-element>
<div class="wrap">
<div name="item">item 1</div>
<div name="item">item 2</div>
</div>
</custom-element>
我将如何包装开槽元素以输出类似于:
<custom-element>
<div class="wrap">
<div name="item">item 1</div>
</div>
<div class="wrap">
<div name="item">item 2</div>
</div>
</custom-element>
我目前的(有缺陷的)方法:
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>
`;
}
});
我建议您改变 children 的包装方法。最简单的方法是将缺少的 HTML 添加为开槽 div 的 child,如下例所示。您仍然可以使用 ::slotted
伪选择器来设置开槽元素的样式。
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>
这种方法背后的原因是换行最终属于 child 元素并且应该与每个 child 一起出现。结果将类似于您的要求。
尽管如此,如果您确实想动态地向元素添加环绕,那么您可以使用 slotchange
事件。每当一个槽被填充并且可以从 ShadowRoot
元素被监听时,该事件就会被触发。在 assignedElements
(槽中的元素)的事件回调循环中,改变它们的 innerHTML
值。
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) {
element.innerHTML = `
<div class="wrap">
${element.textContent}
</div>
`;
}
});
}
});
.wrap {
border: 1px solid black;
padding: 15px;
}
<custom-element>
<div slot="item">item 1</div>
<div slot="item">item 2</div>
</custom-element>
有没有办法将插槽中的每个单独元素包装到特定插槽名称的影子 dom 中?
假设标记与此相似
<custom-element>
<div name="item">item 1</div>
<div name="item">item 2</div>
</custom-element>
目前,渲染类似于:
<custom-element>
<div class="wrap">
<div name="item">item 1</div>
<div name="item">item 2</div>
</div>
</custom-element>
我将如何包装开槽元素以输出类似于:
<custom-element>
<div class="wrap">
<div name="item">item 1</div>
</div>
<div class="wrap">
<div name="item">item 2</div>
</div>
</custom-element>
我目前的(有缺陷的)方法:
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>
`;
}
});
我建议您改变 children 的包装方法。最简单的方法是将缺少的 HTML 添加为开槽 div 的 child,如下例所示。您仍然可以使用 ::slotted
伪选择器来设置开槽元素的样式。
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>
这种方法背后的原因是换行最终属于 child 元素并且应该与每个 child 一起出现。结果将类似于您的要求。
尽管如此,如果您确实想动态地向元素添加环绕,那么您可以使用 slotchange
事件。每当一个槽被填充并且可以从 ShadowRoot
元素被监听时,该事件就会被触发。在 assignedElements
(槽中的元素)的事件回调循环中,改变它们的 innerHTML
值。
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) {
element.innerHTML = `
<div class="wrap">
${element.textContent}
</div>
`;
}
});
}
});
.wrap {
border: 1px solid black;
padding: 15px;
}
<custom-element>
<div slot="item">item 1</div>
<div slot="item">item 2</div>
</custom-element>