shadow dom 内的重复命名插槽不起作用

问题描述

我有一个看起来像这样的自定义元素:

class RepeatMe extends HTMLElement {
    constructor() {
        super();
        this.attachShadow({ mode: 'open' });

        let slot = document.createElement('slot');
        slot.setAttribute('name','bar');
        this.shadowRoot.append(slot);

        slot = document.createElement('slot');
        slot.setAttribute('name','content');
        this.shadowRoot.append(slot);

        slot = document.createElement('slot');
        slot.setAttribute('name','bar');
        this.shadowRoot.append(slot);
    }
}

window.customElements.define('repeat-me',RepeatMe);

我使用它如下:

<repeat-me>
    <div slot="bar">I'm a bar</div>
    <div slot="content">I'm some content</div>
</repeat-me>

我想在 shadow dom 中重复插槽 bar(因为开头和结尾的内容相同),但我得到的是只有第一个插槽被渲染并且第二个是空的。我正在尝试使用 shadow dom 插槽做些什么,或者您知道某种方法可以实现这样的目标吗?

解决方法

不,插槽是独一无二的
并且多个 lightDOM 元素可以插入到同一个 SLOT

将 SLOT 视为您的邮箱;您是否希望您的邮件总是复制到另一个邮箱?

如果你有这个要求,你需要一个过滤器来复制电子邮件(或插槽内容)

WebComponents 有 2 个复制选项:

  1. <span slot="bar"> 克隆到 shadowRoot 中的其他(非插槽)DOM 元素

  2. 克隆到新的 lightDOM 元素 <span slot="duplicate_bar1"> 用于新插入的内容

下面的代码为 slotchange 上发生的 <span slot="bar"> 事件提供了两个选项(槽中的新内容)

  1. slotchange 上,插槽内容被克隆到 class="duplicate_bar" (let dups = ...)

  2. slotchange 上创建新的 lightDOM 元素 (let dupslots = ...)

请注意,只有方法 2. 使用 <slot> 功能,您可以使用 :slotted 样式

此代码仅复制内容;它不会清理已删除的 SLOT 内容。

<my-element>
  <span slot="bar"> ONE </span>
  <span slot="bar"> TWO </span>
  <span slot="content"> content </span>
</my-element>
<template id="MY-ELEMENT">
  <style>
    ::slotted(*) { background: lightgreen }
  </style>
  slot bar: <slot name="bar"></slot>
  <br> slot: content: <slot name="content"></slot>
  <br>Duplicate in SPAN: <span class="duplicate_bar"></span>
  <br>Duplicate in B:<b class="duplicate_bar"></b>
  <br>duplicate_bar1:<slot name="duplicate_bar1"></slot>
  <br>duplicate_bar2:<slot name="duplicate_bar2"></slot>
</template>
<script>
  customElements.define('my-element',class extends HTMLElement {
    constructor() {
      let template = id => document.getElementById(id).content.cloneNode(true);
      super().attachShadow({mode: 'open'}).append(template(this.nodeName));
      let slotname = "bar";
      let slot = this.shadowRoot.querySelector(`[name="${slotname}"]`);
      slot.addEventListener("slotchange",(evt) => {
        let assigned = slot.assignedNodes();
        let dups = [...this.shadowRoot.querySelectorAll(".duplicate_" + slotname)];
        let dupslots = [...this.shadowRoot.querySelectorAll(`slot[name*="duplicate_bar"]`)];
        assigned.forEach(node => {
          dups.forEach(el => el.append(node.cloneNode(true)));
          dupslots.forEach(duplicateslot => {
            let newNode = node.cloneNode(true);
            newNode.slot = duplicateslot.name; // set BEFORE adding to DOM! otherwise 'bar' slotchange Events triggers on it
            this.append(newNode);
          })
        });
      });
    }
  });
</script>