テンプレート、スロット、シャドウ

ウェブ コンポーネントの利点は再利用性であり、UI ウィジェットを一度作成すれば、何度でも再利用できます。この間、 ウェブ コンポーネントを作成するために JavaScript が必要で、JavaScript ライブラリも必要ありません。HTML と関連 API には、必要なすべてのものが用意されています。

ウェブ コンポーネント標準は、HTML テンプレートカスタム要素Shadow DOM などがあります。 これらを組み合わせることで、シームレスに統合できる、カスタマイズされた自己完結型(カプセル化された)再利用可能な要素の構築が可能になります。 すでに説明した他のすべての HTML 要素と同様に、既存のアプリケーションに実装できます。

このセクションでは、<star-rating> 要素を作成します。これは、ユーザーがウェブサイト エクスペリエンスを評価できるウェブ コンポーネントです。 5 段階の星評価で表します。カスタム要素に名前を付ける場合は、すべて小文字にすることをおすすめします。ダッシュや 標準の要素とカスタム要素を区別しやすくなっています。

<template> 要素と <slot> 要素、slot 属性、JavaScript を使用して、 カプセル化された Shadow DOM です。次に、定義した要素を再利用し、テキストのセクションをカスタマイズします。 他の要素やウェブ コンポーネントと同じように使用できます。また、カスタム要素の内部と外部での CSS の使用についても簡単に説明します。

<template> 要素

<template> 要素は、JavaScript でクローンを作成して DOM に挿入する HTML フラグメントを宣言するために使用されます。要素のコンテンツはデフォルトではレンダリングされません。JavaScript を使用してインスタンス化されます。

<template id="star-rating-template">
 
<form>
   
<fieldset>
     
<legend>Rate your experience:</legend>
     
<rating>
       
<input type="radio" name="rating" value="1" aria-label="1 star" required />
       
<input type="radio" name="rating" value="2" aria-label="2 stars" />
       
<input type="radio" name="rating" value="3" aria-label="3 stars" />
       
<input type="radio" name="rating" value="4" aria-label="4 stars" />
       
<input type="radio" name="rating" value="5" aria-label="5 stars" />
     
</rating>
   
</fieldset>
   
<button type="reset">Reset</button>
   
<button type="submit">Submit</button>
 
</form>
</template>

<template> 要素の内容は画面に書き込まれないため、<form> とその内容はレンダリングされません。 はい。この Codepen は空白ですが、HTML タブを調べると、<template> マークアップがあるのがわかります。

この例では、<form> は DOM 内の <template> の子ではありません。<template> 要素の内容は子要素です。 HTMLTemplateElement.content から返された DocumentFragment の プロパティです。表示されるようにするには、JavaScript を使用してコンテンツを取得し、そのコンテンツを DOM に追加する必要があります。

この簡単な JavaScript では、カスタム要素は作成されていません。この例では、<template> の内容を <body> に追加しています。 コンテンツは、表示可能でスタイル設定可能な DOM の一部になりました。

DOM に表示されている、前の Codepen のスクリーンショット。

1 つ星評価だけのテンプレートの実装を JavaScript に要求することはあまり有用ではありませんが、 カスタマイズできる星評価ウィジェットが便利です。

<slot> 要素

発生ごとにカスタマイズした凡例を表示するスロットが用意されています。HTML には <slot> がある 要素を <template> 内のプレースホルダとして配置し、名前を指定すると「名前付きスロット」を作成します。名前付きスロットを使用すると ウェブ コンポーネント内のコンテンツをカスタマイズできます。<slot> 要素を使用すると、カスタム ディメンションの子を そのシャドウツリー内に挿入する必要があります。

テンプレートでは、<legend><slot> に変更します。

<template id="star-rating-template">
 
<form>
   
<fieldset>
     
<slot name="star-rating-legend">
       
<legend>Rate your experience:</legend>
     
</slot>

name 属性は、その要素に slot 属性があり、その値が スロット名を指定します。カスタム要素に一致するスロットがない場合は、<slot> のコンテンツがレンダリングされます。 そのため、汎用コンテンツには <legend> を含めました。コンテンツのない <star-rating></star-rating> が HTML に含まれているだけで、レンダリングしても問題ありません。

<star-rating>
 
<legend slot="star-rating-legend">Blendan Smooth</legend>
</star-rating>
<star-rating>
 
<legend slot="star-rating-legend">Hoover Sukhdeep</legend>
</star-rating>
<star-rating>
 
<legend slot="star-rating-legend">Toasty McToastface</legend>
 
<p>Is this text visible?</p>
</star-rating>

slot 属性は、使用するグローバル属性の <template> 内の <slot> の内容を置き換えます。このカスタム要素では、slot 属性を持つ要素として <legend> である。必ずしもそうである必要はありません。テンプレートでは、<slot name="star-rating-legend"><anyElement slot="star-rating-legend"> に置き換えられます。 ここで、<anyElement> には任意の要素を指定できます。カスタム要素も指定できます。

未定義の要素

<template> では <rating> 要素を使用しました。これはカスタム要素ではありません。むしろ、未知の要素です。ブラウザ 要素を認識できない場合でも失敗しません。認識されない HTML 要素は、ブラウザで匿名のインラインとして処理されます。 CSS でスタイルを設定できる要素です。<span> と同様に、<rating> 要素と <star-rating> 要素にはユーザー エージェントが適用されていません 定義できます。

<template> とコンテンツはレンダリングされないことに注意してください。<template> は、以下のようなコンテンツを含む既知の要素です。 レンダリングされません。<star-rating> 要素はまだ定義されていません。要素を定義するまで、ブラウザはその要素を表示します。 モデルにフィードできます現時点では、認識されない <star-rating> は匿名のインライン要素として扱われるため、コンテンツは 3 番目の <star-rating> の凡例と <p> は、<span> にある場合と同じように表示されます。

この認識されない要素をカスタム要素に変換する要素を定義しましょう。

カスタム要素

カスタム要素を定義するには JavaScript が必要です。定義されている場合、<star-rating> 要素のコンテンツが シャドウルートには、関連付けるテンプレートのすべてのコンテンツが含まれます。テンプレートの <slot> 要素が置き換えられます。 <star-rating> 内の要素のコンテンツ(slot 属性値が <slot> の名前値と一致する場合) あります。そうでない場合は、テンプレートのスロットの内容が表示されます。

スロットに関連付けられていないカスタム要素(3 番目の <star-rating><p>Is this text visible?</p>)内のコンテンツは、 表示されないため、表示されません。

star-rating という名前のカスタム要素を定義します。 HTMLElement を拡張します。

customElements.define('star-rating',
 
class extends HTMLElement {
    constructor
() {
      super
(); // Always call super first in constructor
     
const starRating = document.getElementById('star-rating-template').content;
     
const shadowRoot = this.attachShadow({
        mode
: 'open'
     
});
      shadowRoot
.appendChild(starRating.cloneNode(true));
   
}
 
});

要素が定義されたので、ブラウザが <star-rating> 要素を見つけるたびに、定義されたとおりにレンダリングされます。 テンプレートである #star-rating-template を使用して、要素で検証します。ブラウザは Shadow DOM ツリーをノードにアタッチし、 その Shadow DOM にテンプレート コンテンツのクローンが作成されます。 なお、attachShadow() を設定できる要素には制限があります

const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot
.appendChild(starRating.cloneNode(true));

デベロッパー ツールを見ると、<template><form> が各カスタム要素の Shadow ルートの一部であることがわかります。 <template> コンテンツのクローンがデベロッパー ツールの各カスタム要素に表示され、ブラウザに表示されますが、コンテンツは カスタム要素自体は画面にレンダリングされません

各カスタム要素のテンプレートのクローン内容を示す DevTools のスクリーンショット。

<template> の例では、テンプレートのコンテンツをドキュメントの本文に追加し、通常の DOM にコンテンツを追加しました。 customElements 定義では、同じものを使用しました。 appendChild() ですが、クローン テンプレートの内容が カプセル化された Shadow DOM です。

スターの表示スタイルが未設定のラジオボタンになっていることに注目してください。標準の DOM ではなく Shadow DOM の一部であるため、Codepen の [CSS] タブ内のスタイル設定は適用されません。そのタブの CSS スタイルのスコープは Shadow DOM ではなくドキュメントに設定されているため、スタイルは適用されません。2 つの Cloud Storage バケットを スタイルを使用して、カプセル化された Shadow DOM コンテンツをスタイル設定します。

Shadow DOM

Shadow DOM は、CSS スタイルのスコープを各 Shadow ツリーに制限して、ドキュメントの他の部分から分離します。つまり外部の CSS です コンポーネントのスタイルはコンポーネントには適用されず、コンポーネントのスタイルはドキュメントの残りの部分には影響しません。ただし、 特定します

コンテンツを Shadow DOM に追加したので、<style> 要素を含めることができます。 カプセル化された CSS をカスタム要素に提供します。

スコープがカスタム要素であるため、ドキュメントの他の部分にスタイルが反映されることを心配する必要はありません。Google では、 セレクタの特異性が大幅に低下します。たとえば、カスタム要素で使用される入力はラジオのみであるため、 ボタンの場合は、セレクタとして input[type="radio"] の代わりに input を使用できます。

 <template id="star-rating-template">
 
<style>
    rating
{
     
display: inline-flex;
   
}
    input
{
     
appearance: none;
     
margin: 0;
     
box-shadow: none;
   
}
   
input::after {
     
content: '\2605'; /* solid star */
     
font-size: 32px;
   
}
   
rating:hover input:invalid::after,
   
rating:focus-within input:invalid::after {
     
color: #888;
   
}
   
input:invalid::after,
     
rating:hover input:hover ~ input:invalid::after,
     
input:focus ~ input:invalid::after  {
     
color: #ddd;
   
}
   
input:valid {
     
color: orange;
   
}
   
input:checked ~ input:not(:checked)::after {
     
color: #ccc;
     
content: '\2606'; /* hollow star */
   
}
 
</style>
 
<form>
   
<fieldset>
     
<slot name="star-rating-legend">
       
<legend>Rate your experience:</legend>
     
</slot>
     
<rating>
       
<input type="radio" name="rating" value="1" aria-label="1 star" required/>
       
<input type="radio" name="rating" value="2" aria-label="2 stars"/>
       
<input type="radio" name="rating" value="3" aria-label="3 stars"/>
       
<input type="radio" name="rating" value="4" aria-label="4 stars"/>
       
<input type="radio" name="rating" value="5" aria-label="5 stars"/>
     
</rating>
   
</fieldset>
   
<button type="reset">Reset</button>
   
<button type="submit">Submit</button>
 
</form>
</template>

ウェブ コンポーネントは in-<template> マークアップでカプセル化され、CSS スタイルは Shadow DOM にスコープが設定されて非表示になっていますが、 コンポーネント、レンダリングされるスロット コンテンツ、<anyElement slot="star-rating-legend"> の部分である <star-rating> はカプセル化されません。

現在のスコープ外のスタイル設定

Shadow DOM 内からドキュメントのスタイルを設定し、Shadow DOM のコンテンツを グローバル スタイルです。Shadow DOM が終了して通常の DOM が開始される Shadow の境界線は通過できますが、 あります。

Shadow ツリーは、Shadow DOM 内の DOM ツリーです。シャドウルートは、シャドウツリーのルートノードです。

:host 疑似クラスは、シャドウホスト要素である <star-rating> を選択します。 Shadow ホストは、Shadow DOM が接続されている DOM ノードです。特定のバージョンのホストのみをターゲットにするには、:host() を使用します。 これにより、クラスや属性セレクタなど、渡されたパラメータに一致するシャドウホスト要素のみが選択されます。以下を選択します。 すべてのカスタム要素を表示するには、グローバル CSS で star-rating { /* styles */ } を使用するか、テンプレート スタイルで :host(:not(#nonExistantId)) を使用します。期間 特異性がある場合、グローバル CSS が優先されます。

::slotted() 疑似要素が Shadow DOM の境界を越えています Shadow DOM 内からは使用できません。セレクタに一致する場合、スロット付きの要素を選択します。この例では、::slotted(legend) は 3 つの凡例に一致します。

グローバル スコープの CSS から Shadow DOM をターゲットに設定するには、テンプレートを編集する必要があります。part 属性は、スタイルを設定する要素に追加できます。次に、::part() 疑似要素を使用します。 渡されたパラメータに一致するシャドウツリー内の要素を照合します。疑似要素のアンカー要素または元要素 ホストまたはカスタム要素名(この場合は star-rating)。このパラメータは part 属性の値です。

テンプレートのマークアップが次のように始まった場合:

<template id="star-rating-template">
 
<form part="formPart">
   
<fieldset part="fieldsetPart">

次のように <form><fieldset> をターゲットにできます。

star-rating::part(formPart) { /* styles */ }
star-rating::part(fieldsetPart) { /* styles */ }

パーツ名はクラスと同様に機能します。1 つの要素に複数のスペース区切りのパーツ名を含めることができ、複数の要素は パーツ名が同じです。

Google では、カスタム要素の作成に役立つチェックリストを用意しています。その他の情報 宣言型 Shadow DOM をご覧ください。

理解度をチェックする

テンプレート、スロット、シャドウに関する知識をテストします。

デフォルトでは、Shadow DOM の外部のスタイルは、内部の要素にスタイルを設定します。

誤り。
正しい

<template> 要素の説明として正しいものは、次のうちどれですか。

デフォルトではレンダリングされない HTML のフラグメントを宣言するために使用する要素。
プレースホルダ要素。
ページのコンテンツを表示するために使用される汎用要素。