<template>: コンテンツテンプレート要素
Baseline Widely available *
This feature is well established and works across many devices and browser versions. It’s been available across browsers since 2015年11月.
* Some parts of this feature may have varying levels of support.
<template> は HTML の要素で、HTML のフラグメントを保持し、後で JavaScript を使用して使用したり、シャドウ DOM の中に直接生成したりするためのメカニズムとして機能します。
属性
この要素にはグローバル属性があります。
shadowrootmode-
親要素のシャドウルートを生成します。 これは
Element.attachShadow()メソッドの宣言版で、同じ列挙値を受け入れます。メモ: HTML パーサーは、この属性が設定されているノードの最初の
<template>に対して、DOM にShadowRootオブジェクトを作成します。 この属性が設定されていない場合、または許可された値が設定されていない場合、あるいはShadowRootが既に同じ親に宣言的に作成されている場合は、HTMLTemplateElementが作成されます。HTMLTemplateElementは、HTMLTemplateElement.shadowRootModeを設定したりすることで、解釈後にシャドウルートに変更することはできません。メモ: 古いチュートリアルや例では、Chrome 90-110 で対応していた非標準の
shadowroot属性が見つかるかもしれません。この属性は削除され、標準のshadowrootmode属性に置き換えられています。 shadowrootclonable-
この要素を使用して作成した
ShadowRootのclonableプロパティの値をtrueに設定します。 設定されている場合、シャドウホスト(この<template>の親要素)の複製をNode.cloneNode()またはDocument.importNode()で作成すると、コピーにシャドウルートが含まれます。 shadowrootdelegatesfocus-
この要素を使用して作成した
ShadowRootのdelegatesFocusプロパティの値をtrueに設定します。 これが設定されていて、シャドウツリー内のフォーカス可能でない要素が選択されている場合、フォーカスはツリー内の最初のフォーカス可能な要素に譲られます。 この値はfalseが既定値です。 shadowrootserializable-
この要素を使用して作成した
ShadowRootのserializableプロパティの値をtrueに設定します。 設定されている場合、シャドウルートはElement.getHTML()またはShadowRoot.getHTML()メソッドを、options.serializableShadowRoots引数をtrueに設定して呼び出すことでシリアライズされます。 この値はfalseが既定値です。
使用上のメモ
この要素には、許可されているコンテンツはありません。中に含まれる HTML ソースは、実際には <template> 要素の子になるわけではないからです。 <template> 要素の Node.childNodes プロパティは常に空であり、内側のコンテンツのように見えるものにアクセスするには、特殊な content プロパティを使用します。Node.appendChild() または同様のメソッドを <template> 要素に対して呼び出すと、その <template> 要素自身の子を挿入することになり、コンテンツモデル違反となる上、実際には content プロパティから返される DocumentFragment が更新されません。
<template> 要素の構文解析の仕組み上、テンプレート内の <html>、<head>、<body> の開始タグおよび終了タグは、すべてパーサーから構文エラーとして無視されます。したがって、<template><head><title>Test</title></head></template> は <template><title>Test</title></template> と同等です。
<template> 要素の用途は主に 2 つあります。
テンプレート文書フラグメント
既定では、要素のコンテンツはレンダリングされません。 対応する HTMLTemplateElement インターフェイスは、標準で content プロパティを含みます(同等の content/markup 属性はありません)。この content プロパティは読み取り専用で、テンプレートが表す DOM サブツリーを格納する DocumentFragment を保持します。 このフラグメントは cloneNode メソッドで複製し、DOM に挿入することができます。
content プロパティを使用するときは、返値の DocumentFragment が予期せぬ挙動を示すことがあることに注意が必要です。 詳細は下記の DocumentFragment の落とし穴を避ける節を参照してください。
宣言的シャドウ DOM
もし <template> 要素が shadowrootmode 属性の値 open または closed を格納すると、HTML パーサーは直ちにシャドウ DOM を生成します。その要素は ShadowRoot でラップされたコンテンツによって DOM 内で置き換えられ、親要素に装着されます。 これは Element.attachShadow() を呼び出して要素にシャドウルートを付けるのと宣言的に等価です。
要素が shadowrootmode に他の値を示す場合、または shadowrootmode 属性を持たない場合、パーサーは HTMLTemplateElement を生成します。 同様に、宣言的シャドウルートが複数ある場合、最初のシャドウルートのみが ShadowRoot で置き換えられ、それ以降は HTMLTemplateElement オブジェクトとして解釈できます。
例
>表の行を生成
まず、HTML 部分の例から始めましょう。
<table id="producttable"> <thead> <tr> <td>UPC_Code</td> <td>Product_Name</td> </tr> </thead> <tbody> <!-- 既存のデータは、必要に応じてここに含めることができます --> </tbody> </table> <template id="productrow"> <tr> <td class="record"></td> <td></td> </tr> </template> まず、JavaScript コードを使用して後からコンテンツを挿入するための表を作ります。次に、1 行分を表す HTML 断片の構造が書かれたテンプレートが続きます。
表が生成され、テンプレートが定義されました。 JavaScript を使って、テンプレートを基に構築される各行を表に挿入します。
// template 要素の content 属性の有無を確認することで、 // ブラウザーが HTML の template 要素に対応しているかテストします。 if ("content" in document.createElement("template")) { // 既存の HTML tbody と template の行を使って // table をインスタンス生成します。 const tbody = document.querySelector("tbody"); const template = document.querySelector("#productrow"); // 新しい行を複製して表に挿入します。 const clone = template.content.cloneNode(true); let td = clone.querySelectorAll("td"); td[0].textContent = "1235646565"; td[1].textContent = "Stuff"; tbody.appendChild(clone); // 新しい行を複製して表に挿入します。 const clone2 = template.content.cloneNode(true); td = clone2.querySelectorAll("td"); td[0].textContent = "0384928528"; td[1].textContent = "Acme Kidney Beans 2"; tbody.appendChild(clone2); } else { // HTML の template 要素に対応していないので // 表に行を追加するほかの方法を探します。 } 結果として、 JavaScript を通して、新しい行が追加された HTML の表ができます。
宣言的シャドウ DOM の実装
この例では、マークアップの始めに非表示で対応する警告を記載しています。この警告は後でブラウザーの shadowrootmode 属性に対応していない場合に JavaScript で表示するように設定します。次の記事には 2 つの <article> 要素があり、それぞれ異なる振る舞いをする <style> 要素を含んでいます。最初の <style> 要素は文書全体に対してグローバルです。2 つ目は shadowrootmode 属性が存在するため、<template> 要素の代わりに生成されたシャドウルートにスコープされます。
<p hidden> ⛔ このブラウザーはまだ <code>shadowrootmode</code> 属性に対応していません。 </p> <article> <style> p { padding: 8px; background-color: wheat; } </style> <p>I'm in the DOM.</p> </article> <article> <template shadowrootmode="open"> <style> p { padding: 8px; background-color: plum; } </style> <p>I'm in the shadow DOM.</p> </template> </article> const isShadowRootModeSupported = Object.hasOwn( HTMLTemplateElement.prototype, "shadowRootMode", ); document .querySelector("p[hidden]") .toggleAttribute("hidden", isShadowRootModeSupported); フォーカスを譲渡を伴う宣言的シャドウ DOM
この例では、shadowrootdelegatesfocus を宣言的に作成したシャドウルートに適用し、フォーカスにどのような効果があるかを示します。
このコードでは、最初に <template> 要素に shadowrootmode 属性を用いて、<div> 要素の中にシャドウルートを宣言します。 これにより、テキストを格納したフォーカスできない <div> と、フォーカスできる <input> 要素の両方が表示されます。 また、:focus を持つ要素を青にスタイル設定し、ホスト要素の通常のスタイル設定を設定するには CSS を使用します。
<div> <template shadowrootmode="open"> <style> :host { display: block; border: 1px dotted black; padding: 10px; margin: 10px; } :focus { outline: 2px solid blue; } </style> <div>クリック可能なシャドウ DOM テキスト</div> <input type="text" placeholder="シャドウ DOM 内の入力" /> </template> </div> 2 つ目のコードブロックは、shadowrootdelegatesfocus 属性を設定している以外は同じです。この属性は、ツリー内のフォーカス可能でない要素が選択された場合に、ツリー内の最初のフォーカス可能な要素にフォーカスを譲るものです。
<div> <template shadowrootmode="open" shadowrootdelegatesfocus> <style> :host { display: block; border: 1px dotted black; padding: 10px; margin: 10px; } :focus { outline: 2px solid blue; } </style> <div>クリック可能なシャドウ DOM テキスト</div> <input type="text" placeholder="シャドウ DOM 内の入力" /> </template> </div> 最後に、以下の CSS を使用して、親要素である <div> にフォーカスがあるときに赤の枠線を適用します。
div:focus { border: 2px solid red; } その結果を下記に示します。 HTML は最初にレンダリングされるとき、最初の画像に示すように要素にはスタイル設定がありません。 shadowrootdelegatesfocus が設定されていないシャドウルートでは、<input> 以外の場所をクリックしてもフォーカスは変わりません(<input> 要素を選択すると 2 つ目の画像のようになります)。

shadowrootdelegatesfocus を設定したシャドウルートでは、テキスト(フォーカスできない)をクリックすると、<input> 要素が選択されます。 これは下記に示すように親要素もフォーカスされます。

DocumentFragment の落とし穴の回避
DocumentFragment の値が渡されると、Node.appendChild や同様のメソッドはその値の子ノードだけを対象とするノードに移動させます。したがって、イベントハンドラーは DocumentFragment 自体ではなく、DocumentFragment の子ノードに設定することが推奨されます。
以下の HTML および JavaScript を考えてみてください。
HTML
<div id="container"></div> <template id="template"> <div>クリックしてください</div> </template> JavaScript
const container = document.getElementById("container"); const template = document.getElementById("template"); function clickHandler(event) { event.target.append(" — この div がクリックされました"); } const firstClone = template.content.cloneNode(true); firstClone.addEventListener("click", clickHandler); container.appendChild(firstClone); const secondClone = template.content.cloneNode(true); secondClone.children[0].addEventListener("click", clickHandler); container.appendChild(secondClone); 結果
firstClone は DocumentFragment なので、appendChild が呼び出されたときに container に追加されるのはその子ノードだけで、firstClone のイベントハンドラーはコピーされません。一方、secondClone は最初の子ノードにイベントハンドラーが追加されているため、appendChild が呼び出されるとイベントハンドラーがコピーされ、クリックすると期待通りに動作します。
技術的概要
| コンテンツカテゴリー | メタデータコンテンツ, フローコンテンツ, 記述コンテンツ, スクリプト対応要素 |
|---|---|
| 許可されている内容 | なし(使用上のメモを参照) |
| タグの省略 | なし。開始タグと終了タグの両方が必須です。 |
| 許可されている親要素 | メタデータコンテンツ, 記述コンテンツ, スクリプト対応要素 を受け付けるすべての要素。 また、span 属性を持たない <colgroup> 要素の子になることもできます。 |
| 暗黙の ARIA ロール | 対応するロールなし |
| 許可されている ARIA ロール | 許可されている role なし |
| DOM インターフェイス | HTMLTemplateElement |
仕様書
| Specification |
|---|
| HTML> # the-template-element> |
ブラウザーの互換性
関連情報
partおよびexportparts属性<slot>要素:host、:host()、:host-context()擬似クラス::part、::slotted擬似要素ShadowRootインターフェイス- テンプレートとスロットの使用
- CSS スコープ化モジュール
- 宣言的シャドウ DOM (HTML による) (シャドウ DOM の使用)
- Declarative shadow DOM (web.dev, 2023)