HTML / Element

template要素の使い方

HTMLのtemplate要素は、あとで使うHTMLのひな形をページの中に用意しておくための要素です。template自身は画面に表示されず、必要になったときに中身をコピーしてページへ追加します。

先に結論

templateは、同じ形のカードや行を何度も作るときに便利です。中身はtemplate.contentというDocumentFragmentに入り、cloneNode(true)でコピーしてから表示します。

ひな形を置いただけでは表示されません。コピーした内容をappendなどでページへ追加する処理が必要です。

最小の例

<template id="user-card-template">
  <article>
    <h2 data-name></h2>
    <p data-role></p>
  </article>
</template>

<section id="users"></section>

<script>
  const template = document.querySelector('#user-card-template');
  const card = template.content.cloneNode(true);
  card.querySelector('[data-name]').textContent = 'Aki';
  card.querySelector('[data-role]').textContent = 'デザイナー';
  document.querySelector('#users').append(card);
</script>

最初にtemplateを取得し、contentを深くコピーします。コピーした断片に文字を入れてからページへ追加すると、カードが表示されます。

templateの中身はどこにある?

templateの中に書いた要素は、通常の親子関係でtemplateの子になるわけではありません。ひな形の中身はtemplate.contentから取得します。

const template = document.querySelector('#user-card-template');

template.childNodes.length;         // 0
template.content.childNodes.length; // 1

const copy = template.content.cloneNode(true);
document.querySelector('#users').append(copy);

trueを渡すと、見出しや説明文などの子孫も一緒にコピーされます。コピーしたDocumentFragmentを追加すると、その中の要素がページのDOMになります。

実際にカードを追加する

ボタンを押すと、同じひな形からカードを追加します。

まだカードはありません。

    この例では、画面に置かれているのはボタンと空のリストです。templateの内容は、ボタンを押してコピーを追加したときに初めて表示されます。

    データを入れるときの注意

    ユーザー名や外部データをひな形へ入れるときは、通常の文字列ならtextContentを使います。HTMLとして解釈させる必要がない文字列をinnerHTMLへ入れると、意図しない要素やスクリプトを作る危険があります。

    • 繰り返し使うひな形には、役割が分かるiddata-*属性を付ける。
    • 同じひな形を複数回追加する場合、固定したidを重複させない。
    • 表示に必要な見出し、ラベル、説明は、コピー後のDOMにも残るようにする。
    • ひな形を使うJavaScriptが失敗した場合に、必要な情報が完全に消えないか確認する。

    templateと非表示の要素の違い

    display: noneにした要素は、通常のDOMとして存在しながら、表示やアクセシビリティの扱いが変わります。一方、templateの中身は、そもそも通常のページの子要素として表示される状態ではありません。あとでコピーするひな形を保存したいときにtemplateを使います。

    Declarative Shadow DOMについて

    templateには、shadowrootmode属性を使ってDeclarative Shadow DOMを宣言する高度な使い方もあります。これは通常のカード作成より複雑で、Shadow DOMの境界、slot、focus、アクセシビリティを一緒に確認する必要があります。

    <my-card>
      <template shadowrootmode="open">
        <slot></slot>
      </template>
      カードの本文
    </my-card>

    まずはcontent.cloneNode(true)を使う通常のひな形から理解し、Shadow DOMの仕様や実装差まで知りたいときはAtlasを参照してください。

    よくある間違い

    • templateを書いただけで、画面に中身が表示されると思う。
    • template.querySelector()で、ひな形の中の要素を直接探せると思う。
    • cloneNode(true)を使わず、同じノードを何度も追加できると思う。
    • コピーした内容に同じidが増え、ラベルやリンクの参照が壊れる。
    • ひな形が入力値を安全にしてくれる、またはtemplateがサニタイザーになると思う。

    Atlasで詳しく確認する

    Template Contents、DocumentFragment、parser、Declarative Shadow DOM、Accessibility、ブラウザー実装の確認条件まで詳しく知りたい場合は、Yugien Atlasのtemplate要素を参照してください。