この翻訳は不完全です。英語から この記事を翻訳 してください。

この記事では shadow DOM で使われる <template><slot> 要素の使い方を説明します。

The truth about templates

あるWebページ上で同じ構造を繰り返し使用する必要がある場合、同じ実装を繰り返し書くよりも、テンプレートのようなものを作って利用する方が合理的でしょう。これは以前から可能でしたが、<template> 要素より簡単に使えるようになりました。 この要素と中身は DOM 上ではレンダリングされませんが、JavaScript から参照することができます。

以下の簡単なサンプルを見てみましょう。

<template id="my-paragraph">
  <p>My paragraph</p>
</template>

テンプレートの内部はページには表示されません。以下のコードのようにJavaScript を用いて参照を取り DOM に追加するとページ上に表示できます。

let template = document.getElementById('my-paragraph');
let templateContent = template.content;
document.body.appendChild(templateContent);

つまらない例ですがすでに有用性は見えてきたでしょう。

Using templates with web components

テンプレートはそれ自身でも有用ですが web コンポーネントと共に使用することでより上手く使えます。テンプレートを shadow DOM として活用する web コンポーネントを <my-paragraph> と名付け定義しましょう。

customElements.define('my-paragraph',
  class extends HTMLElement {
    constructor() {
      super();
      let template = document.getElementById('my-paragraph');
      let templateContent = template.content;

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

ここで、テンプレートの内容を使用するために Node.cloneNode() を使用してクローンしたものを shadow root に追加していることに注意してください。

テンプレートの内容を shadow DOM に追加しているので、テンプレートの内部に <style> 要素を用意しスタイルを含むことができます。このスタイルはカスタム要素の内部でカプセル化されます。これは通常の DOM に追加するだけでは正しく動きません。

したがって、例えば

<template id="my-paragraph">
  <style>
    p {
      color: white;
      background-color: #666;
      padding: 5px;
    }
  </style>
  <p>My paragraph</p>
</template>

こうすれば HTML ドキュメントに以下を追加することで使用できます。

<my-paragraph></my-paragraph>

Note: Templates are well-supported in browsers; the Shadow DOM API is supported by default in Firefox (version 63, currently Developer Edition), Chrome, Opera, and Safari. Edge is working on an implementation as well.

Adding flexibility with slots

ここまでのサンプルでは高々1種類のテキストを表示できるのみで、普通の paragraph よりも使えません。<slot> 要素を用いることで、各要素のインスタンスに異なるテキストを表示させることができます。<slot>  は <template> よりサポートが限られており、Chrome 53以降、Opera 40以降、Safari 10以降、Firefox 59以降で実装されていますが、Edge ではまだサポートされていません。slot はその name 属性で区別されており、template の中で任意のマークアップで slot の内容のデフォルト値を埋めることができます。

上記の例に slot を追加することを考えます。パラグラフの要素を以下のように書くことができます。

<p><slot name="my-text">デフォルトテキスト</slot></p>

slot の内容が定義されていない場合や、ブラウザが slot をサポートしていな場合は <my-paragraph> は fallback コンテンツを保持し、このサンプルの場合では "デフォルトテキスト" を表示させることになります。

内容を定義したい slot の名前を slot 属性に設定した要素を <my-paragraph> の中に用意すると、その中身が slot の内容になります。中身は HTML 構造を持つ任意のもので埋めることができます。

<my-paragraph>
  <span slot="my-text">新しいテキストを代入します</span>
</my-paragraph>

以下のようにも設定できます。

<my-paragraph>
  <ul slot="my-text">
    <li>新しいテキストを代入します</li>
    <li>リストも代入できます</li>
  </ul>
</my-paragraph>

Note: Elements that can be inserted into slots are known as Slotable; when an element has been inserted in a slot, it is said to be slotted.

簡単なサンプルでの説明は以上です。他にも実装してみたい場合は、GitHub上のサンプルコードをご利用ください(実行例)。

A more involved example

他の例もみてみましょう。

これからのコードは <slot> を <template> と共に使用する方法の例です。以下の2点を目指す JavaScript です。

  • shadow root の中で <element-details> 要素を slot を用いて作ること。
  • <element-details> 要素を、その shadow root と一緒にレンダリングされるように作ること。つまり、要素の内容が slots の中身に代入されるようになります。

<slot> 要素は <template> 要素なしで使用することが可能です。例えば、 <div> 要素の中で宣言しても Shadow DOM で使用した場合と同様にプレースホルダーとしての役割は果たします。しかし、<template> 要素の中で使用する方がより一般的で実用的です。

テンプレートを利用したコンテナの目的は <template> を使用することで意味的にわかりやすくすることです。さらに、<template> の中には <td> など直接追加して良い要素があり、これらは <div> 要素の中に追加された場合は消えます。

Note: element-detailsの完全なコードはここから見ることができます (実行例)。

template を slot と共に作成する

まず最初に<template> 要素の中に <slot> 要素を作成し、新しい "element-details-template" と名付けたフラグメントを作ります。

<template id="element-details-template">
  <style>
  details {font-family: "Open Sans Light",Helvetica,Arial}
  .name {font-weight: bold; color: #217ac0; font-size: 120%}
  h4 { margin: 10px 0 -8px 0; }
  h4 span { background: #217ac0; padding: 2px 6px 2px 6px }
  h4 span { border: 1px solid #cee9f9; border-radius: 4px }
  h4 span { color: white }
  .attributes { margin-left: 22px; font-size: 90% }
  .attributes p { margin-left: 16px; font-style: italic }
  </style>
  <details>
    <summary>
      <span>
        <code class="name">&lt;<slot name="element-name">NEED NAME</slot>&gt;</code>
        <i class="desc"><slot name="description">NEED DESCRIPTION</slot></i>
      </span>
    </summary>
    <div class="attributes">
      <h4><span>Attributes</span></h4>
      <slot name="attributes"><p>None</p></slot>
    </div>
  </details>
  <hr>
</template>

この <template> 要素にはいくつかの機能があります。

  • <template> には <style> 要素が実装されており、<template> が作るフラグメントの中のみに適応されるCSSスタイルを定義できます。 
  • <template><slot> を使用しており、それぞれの name 属性は以下のように定義されています。
    • <slot name="element-name">
    • <slot name="description">
    • <slot name="attributes">
  • <template> の中で各 slot は<details> 要素の中に実装されています。

<template> から <element-details> 要素を作る

次に <element-details> と名付けた新しいカスタム要素を作りましょう。 上で確認した簡単な例と同様に、Element.attachShadow を利用してカスタム要素に shadow root を追加します。

customElements.define('element-details',
  class extends HTMLElement {
    constructor() {
      super();
      var template = document
        .getElementById('element-details-template')
        .content;
      const shadowRoot = this.attachShadow({mode: 'open'})
        .appendChild(template.cloneNode(true));
  }
})

slot と共に <element-details> 要素を使う

では <element-details> 要素を実際に使ってみましょう。 

<element-details>
  <span slot="element-name">slot</span>
  <span slot="description">A placeholder inside a web
    component that users can fill with their own markup,
    with the effect of composing different DOM trees
    together.</span>
  <dl slot="attributes">
    <dt>name</dt>
    <dd>The name of the slot.</dd>
  </dl>
</element-details>

<element-details>
  <span slot="element-name">template</span>
  <span slot="description">A mechanism for holding client-
    side content that is not to be rendered when a page is
    loaded but may subsequently be instantiated during
    runtime using JavaScript.</span>
</element-details> 

このコードについて以下の点に注意してください。

  • 2つの <element-details> 要素が使用されており、いずれも slot 属性を "element-name" および "description" と指定することで対応する slot を参照しています。
  • 1つ目の <element-details> 要素でのみ "attributes" と名付けられた slot を参照しています。2個目の <element-details> 要素では参照していません。
  • 1つ目の <element-details> 要素は <dl> 要素を用いて "attributes" と名付けられた slot を参照しています。

スタイルを追加する

最後にもう少しCSSスタイルを追加します。これは、1個目の <element-details> の中で使われている <dl><dt><dd> 要素のために用意されています。 

  dl { margin-left: 6px; }
  dt { font-weight: bold; color: #217ac0; font-size: 110% }
  dt { font-family: Consolas, "Liberation Mono", Courier }
  dd { margin-left: 16px }

Result

以上のコードを繋げてどのような結果がレンダリングされるかを確認しましょう。

ScreenshotLive sample

以下のことに着目してください。

  • Even though the instances of the <element-details> element in the document do not directly use the <details> element, they get rendered using <details> because the shadow root causes them to get populated with that.
  • Within the rendered <details> output, the content in the <element-details> elements fills the named slots from the shadow root. In other words, the DOM tree from the <element-details> elements get composed together with the content of the shadow root.
  • For both <element-details> elements, an Attributes heading gets automatically added from the shadow root before the position of the "attributes" named slot.
  • Because the first <element-details> has a <dl> element which explicitly references the "attributes" named slot from its shadow root, the contents of that <dl> replace the "attributes" named slot from the shadow root.
  • Because the second <element-details> doesn’t explicitly reference the "attributes" named slot from its shadow root, its content for that named slot gets filled with the default content for it from the shadow root.

ドキュメントのタグと貢献者

このページの貢献者: mdnwebdocs-bot, sudame, elkurin
最終更新者: mdnwebdocs-bot,