Edited at

template要素についてのお勉強


はじめに

HTML5の新たな要素としてtemplate要素 <template> がありますが、これを使うと何が便利になり何が嬉しいのか。

自分が後で見返すためのメモとして書き記しておきます。


template要素って何さ

template要素はブラウザ上では表示されず、主にJavaScriptによる操作がメインになる要素です。

名前からもわかるようにテンプレートとして使うコンテンツを中に書いてJavaScriptであれこれできるやつです。

<template id="template">

<p>これはテンプレートだよ</p>
<p><img src="./this_is_template.jpg" alt="これはテンプレートだよ"></p>
<div id="templateContent"></div>
</template>

template要素内のコンテンツは、JavaScriptの操作で本文へと挿入されない限り本文に何も影響を与えません。

つまり、template要素内で <img> で画像を指定していても、挿入されるまでその画像はロードされることはありません。


<script type="text/template"></script> との違い

script要素を type="text/template" 等とする事でテンプレートとして扱うライブラリもありますが、script要素内に書かれたコンテンツはあくまでテキストとして保持されているので、JavaScript側でHTML要素として扱えるような処理が必要になります。

それに対してtemplate要素内に書かれたコンテンツはテキストではなくHTML要素として存在できます。


template要素の使い方

例えば、JSONのデータを元にキャプション付きの画像をたくさん生成するとします。

以下のようなHTMLがあるとします。


HTML

<div id="imageBlock"></div>

<template id="imageTemplate">
<figure>
<img>
<figcaption></figcaption>
</figure>
</template>


JavaScriptでは例えばこのようにしておきます。


JavaScript

// JSONの例

var json = '[{"file":"./image/01.jpg","caption":"キャプション1"},{"file":"./image/02.jpg","caption":"キャプション2"}]';

document.addEventListener('DOMContentLoaded', function () {
var
// template要素からコンテンツを取得、インスタンスの生成
content = document.querySelector('#imageTemplate').content,

// テンプレート内のimg要素
image = content.querySelector('img'),

// テンプレート内のfigcaption要素
caption = content.querySelector('figcaption'),

// フラグメント
fragment = document.createDocumentFragment(),

// JSON文字列を変換
imageData = JSON.parse(json);

imageData.forEach(function (data) {
var clone;

// テンプレートの要素に適用する
image.src = data.file;
image.alt = data.caption;
caption.textContent = data.caption;

// テンプレートのノードを複製
clone = document.importNode(content, true);

// 複製したノードをフラグメントに挿入
fragment.appendChild(clone);
});

// HTMLに挿入
document.querySelector('#imageBlock').appendChild(fragment);
});


結果的にこんな感じのHTMLになります。


ブラウザが解釈するHTML構造

<div id="imageBlock">

<figure>
<img src="./image/01.jpg" alt="キャプション1">
<figcaption>キャプション1</figcaption>
</figure>
<figure>
<img src="./image/02.jpg" alt="キャプション2">
<figcaption>キャプション2</figcaption>
</figure>
</div>

<template id="imageTemplate">
<figure>
<img src="./image/02.jpg" alt="キャプション2">
<figcaption>キャプション2</figcaption>
</figure>
</template>


ここで注目するのは、template要素内の要素までも変わっている点です。

「これはダメなんじゃないの?」と思うかもしれませんが、テンプレートを再利用するにしてもそのまま使用せずに各種値を上書きするはずなので問題ないかと思います。


HTMLTemplateElement.content って何だ

template要素が持つ読み取り専用のプロパティです。

中身は [object DocumentFragment] になっていて、さらにその中にtemplate要素内のHTML要素が納められています。

DocumentFragment オブジェクトとは、親ノードが存在しないひとかたまりの文章構造で、document の軽量版のようなものです。

template要素内の要素は DocumentFragment.getElementById()DocumentFragment.querySelector()DocumentFragment.querySelectorAll() で取得が可能です。

DocumentFragment.getElementsByTagName()DocumentFragment.getElementsByClassName() は使えません。

DocumentFragment - Web API インターフェイス | MDN


document.importNode() って何だ

template要素内のコンテンツは DocumentFragment になっています。

document.importNode() はそれを複製し、文章に挿入できるようにするメソッドです。

もし、これを行わずに本文に appendChild してしまうとtemplate要素内のコンテンツを移動させることになり、template要素内は空っぽになってしまいます。再利用ができない!

document.importNode() - Web API インターフェイス | MDN


jQueryで書けるの?

現状の jQuery (v3.1.1) はtemplate要素の操作は行えないようです。が、使えないこともありません。

jQueryに書き直したバージョンです。


jQueryの場合

// JSONの例

var json = '[{"file":"./image/01.jpg","caption":"キャプション1"},{"file":"./image/02.jpg","caption":"キャプション2"}]';

$(function () {
var
// template要素からコンテンツを取得、インスタンスの生成
content = $('#imageTemplate')[0].content,

// テンプレート内のimg要素
$image = $('img', content),

// テンプレート内のfigcaption要素
$caption = $('figcaption', content),

// フラグメント
fragment = document.createDocumentFragment(),

// JSON文字列を変換
imageData = JSON.parse(json);

imageData.forEach(function (data) {
var clone;

// テンプレートの要素に適用する
$image.prop({
src: data.file,
alt: data.caption,
});
$caption.text(data.caption);

// テンプレートのノードを複製
clone = document.importNode(content, true);

// 複製したノードをフラグメントに挿入
fragment.appendChild(clone);
});

// HTMLに挿入
$('#imageBlock').append(fragment);
});



template要素のメリット・デメリット

自分が思いついたのを挙げただけなので、まだこういうのがあるよ等のツッコミがあればお願いします。


メリット


  • 保守性があがる

  • JavaScript側で要素の生成などを行わなくていい

  • HTMLソース内に書けるので、一目でどのような構造なのかわかる

  • 通常の要素の操作とあまり変わらない


デメリット


  • Internet Explorer は対応していない

  • 他のテンプレートエンジンと違い、テンプレート内で条件分岐などは書けない


おわりに

IE非対応という点からまだまだ本格的に使用できるのはまだ先な気がしますね……