如何单独包装开槽元素

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>