• HTML5のtemplate要素はcontentをcloneNode(true)で複製してから文書へ挿入する

HTML5のtemplate要素はcontentをcloneNode(true)で複製してから文書へ挿入する

公開日: 執筆:佐藤

HTML5のtemplate要素は、繰り返して使うHTMLの形を文書内に用意するための要素です。templateの中身はHTMLとして解析されますが、読み込み時には表示されません。JavaScriptでcontentを取得し、cloneNode(true)で複製し、値を入れてから文書へ挿入します。

商品一覧や店舗一覧を作る場合、文字列を連結してHTMLを組み立てる前に、繰り返す構造をmarkupとして読める形にできます。ただし、JavaScriptが動かなければ必要な情報まで消える設計にはしません。

templateの中身は表示用DOMと分かれている

W3CのHTML5 Recommendationでは、templateを、scriptで複製して文書へ挿入できるHTML fragmentの宣言に使います。中身はDocumentFragmentとしてcontentに保持され、読み込み時の表示には使われません。

<template id="shop-item">
  <li class="shop">
    <strong class="shop-name"></strong>
    <span class="shop-hours"></span>
  </li>
</template>

<ul id="shop-list"></ul>

この段階では、li.shopは画面に表示されません。document.querySelector('#shop-item')で取得したtemplate要素と、template.contentに入っているfragmentを分けて考えます。

contentをdeep cloneしてから値を入れる

次は一店舗分を追加する仮例です。

var template = document.querySelector('#shop-item');
var list = document.querySelector('#shop-list');
var fragment = template.content.cloneNode(true);

fragment.querySelector('.shop-name').textContent = 'サンプル店舗';
fragment.querySelector('.shop-hours').textContent = '10時から18時';

list.appendChild(fragment);

cloneNode(true)のtrueは、fragmentの子孫も含めて複製する指定です。falseではDocumentFragmentだけが複製され、中のli、strong、spanは入りません。

複製したfragmentへ値を入れた後、一覧へ追加します。元のtemplate.contentは残るため、別のデータでも同じ手順を繰り返せます。

template要素そのものではなくcontentを複製する

表示したいのはtemplate要素ではなく、その中に用意したHTML fragmentです。次の順序を固定すると、扱う対象を取り違えにくくなります。

  1. template要素を取得する
  2. template.contentからDocumentFragmentを得る
  3. cloneNode(true)で子孫を含めて複製する
  4. 複製したfragment内の要素へ値を入れる
  5. 表示先へfragmentを挿入する

DOM4のnode挿入手順では、DocumentFragmentを挿入すると、その子nodeをfragmentから取り除いて表示先へ移します。二件目を作るための元の中身が空になるため、繰り返す用途では複製を先に行います。

文字列をHTMLとして解析する必要がなければtextContentを使う

外部dataから得た店舗名や営業時間を入れるだけなら、textContentで文字列として設定します。値をinnerHTMLへ渡すと、その文字列はHTMLとして解析されます。見出しやlinkなどの構造はtemplate側へ書き、差し替えるdataはtextとして入れると、構造と値の責任を分けられます。

fragment.querySelector('.shop-name').textContent = shop.name;
fragment.querySelector('.shop-hours').textContent = shop.hours;

dataの中にmarkupを許す必要がある場合は、誰がそのHTMLを作るか、どの要素と属性を許すかを先に決めます。templateを使うこと自体は、innerHTMLへ渡す値の安全性を保証しません。

template内のformとscriptは挿入前の動作を期待しない

template.contentは、主文書と干渉しないinertなsubtreeとして保持されます。中にform controlを書いても主文書のform送信へ加わらず、scriptを書いてもtemplate内にある間は実行されません。

この性質は、完成前の部品を待機させるために使えます。ただし、文書へ挿入した後の動作は別に設計します。特に同じidを持つ要素を複数回cloneすると、文書内でidが重複します。繰り返す部品の検索や装飾にはclassを使い、固有のidが必要ならcloneごとに値を変えます。

導入前に対象環境とscriptなしの表示を確認する

template要素とcontentを使えるかは、対象browserで実際に確認します。利用者の環境を確認せず、対応範囲をbrowser名だけで決めません。

問い合わせ先や価格など、表示されなければ困る情報は、server側で通常のHTMLとして出す方法も検討します。templateは、dataの到着や利用者の操作に応じて同じ構造を追加する場合に向いています。最初から存在する一件の情報まで、無理にJavaScriptへ移す必要はありません。

最初の一手は、繰り返し生成しているHTML文字列を一か所選び、固定構造と差し替える値に分けることです。固定構造をtemplateへ置き、値だけをclone後のfragmentへ設定します。

戻る