Kodokon kodokon.com

Web Components: カスタム要素、シャドウDOM、スロット

カスタム要素のライフサイクル、シャドウDOMのカプセル化、そしてスロットによるコンテンツ投影を習得します。

12 分 · 3 問

このレッスンを Kodokon で開く

カスタム要素はHTMLの語彙を拡張します。HTMLElementを継承したクラスは、その名前にハイフンが含まれてさえいれば(user-badgeは良いがuserbadgeは不可)、CustomElementRegistryに登録できます。微妙な点: customElements.defineが呼ばれる前にタグがドキュメントに現れた場合、それはまず未知の要素として作成され、その後その場でアップグレードされます。コンストラクターは、すでにツリーに存在するノードに対して後から呼び出されるのです。

JAVASCRIPT
class UserBadge extends HTMLElement {
  static observedAttributes = ["name"];
  connectedCallback() {
    this.render();
  }
  attributeChangedCallback() {
    this.render();
  }
  render() {
    this.textContent = this.getAttribute("name");
  }
}
customElements.define("user-badge", UserBadge);
完全なライフサイクルを備えた最小限のカスタム要素。

ライフサイクルにはいくつかの罠があります。attributeChangedCallbackobservedAttributesにリストされた属性に対してのみ発火し、アップグレード中には、すでに存在するすべての属性についてconnectedCallbackに呼び出されます。仕様はコンストラクターが属性や子要素を検査すること、またそれらを追加することを禁じています。コンストラクターが実行される瞬間(createElementやクローンを介して)、要素は完全に空である可能性があるからです。

HTML
<template id="card-tpl">
  <style>
    :host { display: block; padding: 1rem; }
    ::slotted(h2) { margin: 0; }
  </style>
  <slot name="title"></slot>
  <slot></slot>
</template>
不活性なテンプレート: パースはされるが、実行もレンダリングもされません。

attachShadowはカプセル化されたサブツリーを作成します。スタイルはそこから漏れ出さず、ページのセレクターはその中に届きません。境界を越えるのは継承されるCSSプロパティ(color、font)とカスタムプロパティだけです。<template>の内容は.contentを介してアクセスできるDocumentFragmentに存在します。ドキュメントにクローンするまで、そのスクリプトは実行されず、画像も読み込まれません。ライトDOMに置かれたノードは<slot>要素へと投影されます。それらは物理的にはライトDOMに留まり、slotchangeイベントが割り当ての変更を通知します。

JAVASCRIPT
class InfoCard extends HTMLElement {
  constructor() {
    super();
    const tpl = document.getElementById("card-tpl");
    this.attachShadow({ mode: "open" })
      .appendChild(tpl.content.cloneNode(true));
  }
}
customElements.define("info-card", InfoCard);
テンプレートをオープンなシャドウルートにクローンする。

2つの拡張機能に注目する価値があります。フォーム関連カスタム要素: static formAssociated = trueattachInternals()により、あなたのコンポーネントはフォームに完全に参加します(送信される値、ネイティブのバリデーション、:invalid状態)。そして宣言的シャドウDOM: <template shadowrootmode=open>はJavaScriptなしでパース時にシャドウルートを取り付け、Web componentsをサーバーサイドレンダリングと互換にします。

理解度チェック

このレッスンの要点をしっかり覚えているか確認しましょう。

  1. なぜカスタム要素のコンストラクターは、属性を読んだり子要素を作成したりしてはならないのですか?
    • connectedCallbackの前は属性が読み取り専用に凍結されているから
    • 構築中はシャドウDOMがライトDOMを隠すから
    • その瞬間(createElement、クローン)、要素は空である可能性があり、仕様がコンテンツに影響を与えないコンストラクターを要求するから
  2. attachShadow({ mode: 'closed' })の場合、外部からelement.shadowRootは何を返しますか?
    • null: attachShadowが返した参照だけがシャドウルートへのアクセスを与える
    • シャドウルート、ただし読み取り専用
    • 空のDocumentFragment
    • DOMExceptionがスローされる
  3. <slot>に投影されたノードは物理的にはどこに存在しますか?
    • レンダリング時にシャドウDOMへ移動される
    • ライトDOMに留まる。スロットは単に、平坦化されたツリー内のその位置にそれらを表示するだけ
    • クローンされる: 原本はライトDOMに留まり、コピーがシャドウDOMに存在する