🐥

Web Componentsで子要素を取得したい

2022/11/12に公開

Web Component で子要素を取得するには slot を使おう

Web Components で子要素を取得するには<slot>を使う

ElemntWithChildren.ts
class ElementWithChildren extends HTMLElement {
  connectedCallback() {
    const shadow = this.attachShadow({ mode: "open" });
    shadow.innerHTML = `
        <div>
          <slot></slot>
        </div>
      `;
  }
}

customElements.define("element-with-children", ElementWithChildren);

ts をコンパイルして色々して

index.html
<element-with-children>
  <p>子要素だぞ</p>
</element-with-children>

みたいにするとブラウザには
dom
って出る

開発者ツールから DOM を見てみると
dom
っていう変な感じになってる

slot に名前をつけよう

slot に name 属性をつけると、対応するところに挿入してくれる

UserCard.ts
class UserCard extends HTMLElement {
  connectedCallback() {
    const shadow = this.attachShadow({ mode: "open" });
    shadow.innerHTML = `
      <div>
        Name: <slot name="username"></slot>
      </div>
      <div>
        Name: <slot name="birthday"></slot>
      </div>
    `;
  }
}

customElements.define("user-card", UserCard);
index.html
<user-card>
  <span slot="username">John Smith</span>
  <span slot="birthday">01.01.2001</span>
</user-card>

connectedCallback 内では this.innerHTML で子要素を取得するのはやめよう

connectedCallback 内では innerHTML で子要素を取得するのは多分あまりよくない

これは以下のような理由から:

DOM は親要素 → 子要素の順にレンダリングされる

<parent>
  <child></child>
</parent>

という構成だったら parent のレンダリングは child より先になる

connectedCallback は要素が DOM に挿入されたタイミングで走るけど、そのタイミングでは子要素がまだ作成されていない

<script>
  customElements.define(
    "user-info",
    class extends HTMLElement {
      connectedCallback() {
        alert(this.innerHTML); // connectedCallbackが走るタイミングではinnerHTMLは空
      }
    }
  );
</script>

<user-info>John</user-info>

setTimeout とかで子要素の作成を待つこともできるけど、それだったら素直に slot を使った方がいい

参考

https://ja.javascript.info/slots-composition
https://ja.javascript.info/custom-elements

Discussion