Shadow DOM の操作

Lit コンポーネントは、DOM をカプセル化するために Shadow DOM を使用します。Shadow DOM は、要素に別個の分離されたカプセル化された DOM ツリーを追加する方法を提供します。DOM カプセル化は、ページ上で機能する他のWebコンポーネントやLitコンポーネントを含む、他のコードとの相互運用性を解き放つための鍵です。

Shadow DOM は3つの利点をもたらします。

  • DOM スコープ。document.querySelector のような DOM API は、コンポーネントの Shadow DOM 内の要素を見つけられないため、グローバルスクリプトが誤ってコンポーネントを破壊する可能性が低くなります。
  • スタイルスコープ。残りの DOM ツリーに影響を与えない、Shadow DOM 用のカプセル化されたスタイルを作成できます。
  • コンポジション。内部 DOM を含むコンポーネントのシャドウルートは、コンポーネントの子要素とは別に存在します。子要素をコンポーネントの内部 DOM にどのようにレンダリングするかを選択できます。

Shadow DOM の詳細については

古いブラウザ。ネイティブの Shadow DOM が利用できない古いブラウザでは、Web コンポーネントポリフィル を使用できます。Lit の polyfill-support モジュールは、Web コンポーネントポリフィルと共にロードする必要があることに注意してください。詳細については、「レガシーブラウザの要件」を参照してください。

Lit は、デフォルトでシャドウルートである renderRoot にコンポーネントをレンダリングします。内部要素を見つけるには、this.renderRoot.querySelector() などの DOM クエリ API を使用できます。

renderRoot は、常にシャドウルートまたは要素のいずれかでなければなりません。これらは、.querySelectorAll().children などの API を共有します。

コンポーネントの初回レンダリング後(たとえば、firstUpdated 内で)に内部 DOM をクエリするか、ゲッターパターンを使用できます。

LitElement は、このようなゲッターを定義するための簡潔な方法を提供する一連のデコレーターを提供します。

@query@queryAll、および @queryAsync デコレーターはすべて、内部コンポーネント DOM 内のノードにアクセスするための便利な方法を提供します。

デコレーターの使用。デコレーターは提案されている JavaScript の機能であるため、デコレーターを使用するには、Babel や TypeScript などのコンパイラを使用する必要があります。詳細については、「デコレーターの使用」を参照してください。

クラスプロパティを変更し、レンダリングルートからノードを返すゲッターに変換します。オプションの2番目の引数が true の場合、DOM クエリは一度だけ実行され、結果がキャッシュされます。これは、クエリ対象のノードが変更されない場合のパフォーマンス最適化として使用できます。

このデコレーターは、次のものと同等です。

単一のノードではなく、すべてのノードを返すことを除いて、query と同じです。querySelectorAll を呼び出すことと同等です。

ここでは、_divs はテンプレート内の両方の <div> 要素を返します。TypeScript の場合、@queryAll プロパティの型は NodeListOf<HTMLElement> です。取得するノードの種類が正確にわかっている場合は、より具体的な型にすることができます。

buttons の後の感嘆符(!)は、TypeScript の非nullアサーション演算子です。コンパイラに、buttons は常に定義されており、null でも undefined でもないことを伝えます。

@query と似ていますが、ノードを直接返す代わりに、保留中の要素のレンダリングが完了した後にそのノードに解決される Promise を返します。コードはこれを使用して、updateComplete プロミスを待つ代わりに使用できます。

これは、たとえば、@queryAsync によって返されるノードが、別のプロパティの変更の結果として変更される可能性がある場合に役立ちます。

コンポーネントは子要素を受け入れる場合があります(<ul> 要素が <li> 子要素を持つことができるように)。

デフォルトでは、要素にシャドウツリーがある場合、その子要素はまったくレンダリングされません。

子要素をレンダリングするには、テンプレートに1つ以上の<slot> 要素を含める必要があります。これは、子ノードのプレースホルダーとして機能します。

要素の子要素をレンダリングするには、要素のテンプレートにそれらのための <slot> を作成します。子要素は DOM ツリー内で移動されませんが、<slot> の子要素であるかのようにレンダリングされます。たとえば

子要素を特定のスロットに割り当てるには、子要素の slot 属性がスロットの name 属性と一致するようにします。

  • 名前付きスロットは、一致する slot 属性を持つ子要素のみを受け入れます。

    たとえば、<slot name="one"></slot> は、属性 slot="one" を持つ子要素のみを受け入れます。

  • slot 属性を持つ子要素は、一致する name 属性を持つスロットにのみレンダリングされます。

    たとえば、<p slot="one">...</p><slot name="one"></slot> にのみ配置されます。

スロットのフォールバックコンテンツを指定できます。フォールバックコンテンツは、子要素がスロットに割り当てられていない場合に表示されます。

フォールバックコンテンツのレンダリング。子ノードがスロットに割り当てられている場合、そのフォールバックコンテンツはレンダリングされません。名前のないデフォルトのスロットは、すべての子ノードを受け入れます。たとえば、<example-element> </example-element> のように、割り当てられたノードが空白を含むテキストノードのみである場合でも、フォールバックコンテンツはレンダリングされません。カスタム要素の子要素として Lit 式を使用する場合は、スロットのフォールバックコンテンツがレンダリングされるように、適切な非レンダリング値を使用してください。詳細については、「子コンテンツの削除」を参照してください。

シャドウルート内のスロットに割り当てられた子要素にアクセスするには、標準の slot.assignedNodes メソッドまたは slot.assignedElements メソッドを slotchange イベントと共に使用できます。

たとえば、特定のスロットに割り当てられた要素にアクセスするゲッターを作成できます。

slotchange イベントを使用して、割り当てられたノードが変更されたときにアクションを実行することもできます。次の例では、すべてのスロットされた子要素のテキストコンテンツを抽出します。

詳細については、MDN のHTMLSlotElementを参照してください。

@queryAssignedElements および @queryAssignedNodes デコレーター

「@queryAssignedElements および @queryAssignedNodes デコレーター」へのパーマリンク

@queryAssignedElements@queryAssignedNodes は、クラスプロパティを、コンポーネントのシャドウツリー内の特定のスロットで slot.assignedElements または slot.assignedNodes をそれぞれ呼び出した結果を返すゲッターに変換します。これらを使用して、特定のスロットに割り当てられた要素またはノードをクエリします。

どちらも、次のプロパティを持つオプションのオブジェクトを受け入れます。

プロパティ説明
flatten<slot> 要素をその割り当てられたノードに置き換えることで、割り当てられたノードをフラット化するかどうかを指定するブール値。
スロットクエリ対象のスロットを指定するスロット名です。未定義のままにすると、デフォルトのスロットが選択されます。
selector (queryAssignedElements のみ)指定した場合、このCSSセレクタに一致する割り当て済み要素のみを返します。

どのデコレータを使用するかは、スロットに割り当てられたテキストノードをクエリするのか、要素ノードのみをクエリするのかによって異なります。この決定は、ユースケースに固有です。

デコレーターの使用。デコレーターは提案されている JavaScript の機能であるため、デコレーターを使用するには、Babel や TypeScript などのコンパイラを使用する必要があります。詳細については、「デコレーターの使用」を参照してください。

上記の例は、次のコードと同等です。

各Litコンポーネントには、**レンダリングルート**があります。これは、内部DOMのコンテナとして機能するDOMノードです。

デフォルトでは、LitElementは開いているshadowRootを作成し、その内部でレンダリングを行い、次のDOM構造を生成します。

LitElementで使用されるレンダリングルートをカスタマイズするには、2つの方法があります。

  • shadowRootOptionsの設定。
  • createRenderRootメソッドの実装。

レンダリングルートをカスタマイズする最も簡単な方法は、shadowRootOptions静的プロパティを設定することです。createRenderRootのデフォルトの実装は、コンポーネントのシャドウルートを作成する際に、shadowRootOptionsをオプション引数としてattachShadowに渡します。これは、ShadowRootInitディクショナリで許可されているオプション(例:modedelegatesFocus)をカスタマイズするために設定できます。

詳細は、MDNのElement.attachShadow()を参照してください。

createRenderRootのデフォルトの実装は、開いているシャドウルートを作成し、static stylesクラスフィールドに設定されているスタイルを追加します。スタイルに関する詳細は、スタイルを参照してください。

コンポーネントのレンダリングルートをカスタマイズするには、createRenderRootを実装し、テンプレートをレンダリングするノードを返します。

たとえば、テンプレートをメインのDOMツリーに要素の子としてレンダリングするには、createRenderRootを実装し、thisを返します。

子要素へのレンダリング。子要素へのレンダリングは、シャドウDOMではなく、一般的に推奨されません。要素はDOMまたはスタイルスコープにアクセスできなくなり、内部DOMに要素を合成できなくなります。