7

slot属性を指定することなく、ネストされた Web コンポーネントまたは特定のタイプの要素を自動的またはプログラムでスロットに入れることは可能ですか?

次のような構造を考えてみましょう。

<parent-element>
  <child-element>Child 1</child-element>
  <child-element>Child 2</child-element>

  <p>Content</p>
</parent-element>

次の<parent-element>ような Shadow DOM を使用します。

<div id="child-elements">
  <slot name="child-elements">
    <child-element>Default child</child-element>
  </slot>
</div>
<div id="content">
  <slot></slot>
</div>

期待される結果は次のとおりです。

<parent-element>
  <#shadow-root>
    <div id="child-elements">
      <slot name="child-elements">
        <child-element>Child 1</child-element>
        <child-element>Child 2</child-element>
      </slot>
    </div>
    <div id="content">
      <slot>
        <p>Content</p>
      </slot>
    </div>
</parent-element>

つまり、要素内でのみ許可されるのと同様に<child-element>、 s は要素内でのみ許可されるように強制したいと考えています。そして、それらを要素内に配置したい。それらのそれぞれに属性を指定して、それらを特定のスロット内に配置する必要があるのは冗長に思えます。同時に、 内の残りのコンテンツは、2 番目の要素に自動的に挿入されます。<parent-element><td><tr><slot name="child-elements">slot<parent-element><parent-element><slot>

親要素を登録するときにこれを定義する方法を最初に検索しましたが、CustomElementRegistry.define()現在はオプションとしてのみサポートextendsされています。

次に、要素を手動でスロットに入れることができる関数、つまり のようなものがあるのではないかと思いchildElement.slot('child-elements')ましたが、それは存在しないようです。

次に、次のように、親要素のコンストラクターでプログラムでこれを実現しようとしました。

constructor() {
  super();

  this.attachShadow({mode: 'open'});
  this.shadowRoot.appendChild(template.content.cloneNode(true));

  const childElements = this.getElementsByTagName('child-element');
  const childElementSlot = this.shadowRoot.querySelector('[name="child-elements"]');
  for (let i = 0; i < childElements.length; i++) {
    childElementSlot.appendChild(childElements[i]);
  }
}

これは子要素を に移動しませんが、それらはすべて 2 番目の要素<slot name="child-elements">にスロットされます。<slot>

4

1 に答える 1

8

名前のないデフォルトは、名前付きスロット<slot></slot>に割り当てられていないすべての要素をキャプチャします。イベントはそれらをキャプチャし、正しいスロットに強制でき ます。
slotchangechild-element

customElements.define('parent-element', class extends HTMLElement {
    constructor() {
      super().attachShadow({mode:'open'})
             .append(document.getElementById(this.nodeName).content.cloneNode(true));
      this.shadowRoot.addEventListener("slotchange", (evt) => {
        if (evt.target.name == "") {// <slot></slot> captures
          [...evt.target.assignedElements()]
            .filter(el => el.nodeName == 'CHILD-ELEMENT') //process child-elements
            .map(el => el.slot = "child-elements"); // force them to their own slot
        } else console.log(`SLOT: ${evt.target.name} got:`,evt.target.assignedNodes())
      })}});
customElements.define('child-element', class extends HTMLElement {
    connectedCallback(parent = this.closest("parent-element")) {
      // or check and force slot name here
      if (this.parentNode != parent) {
        if (parent) parent.append(this); // Child 3 !!!
        else console.error(this.innerHTML, "wants a PARENT-ELEMENT!");
      }}});
child-element { color: red; display: block; } /* style lightDOM in global CSS! */
<template id=PARENT-ELEMENT>
  <style>
    :host { display: inline-block; border: 2px solid red; }
    ::slotted(child-element) { background: lightgreen }
    div { border:3px dashed rebeccapurple }
  </style>
  <div><slot name=child-elements></slot></div>
  <slot></slot>
</template>

<parent-element>
  <child-element>Child 1</child-element>
  <child-element>Child 2</child-element>
  <b>Content</b>
  <div><child-element>Child 3 !!!</child-element></div>
</parent-element>
<child-element>Child 4 !!!</child-element>

の直接の子では<child-element>ない処理のロジックに注意してください。おそらく、これを自分のニーズに合わせて書き直す必要があります。<parent-element>

于 2020-12-23T12:59:18.810 に答える