<template>: 內容模板元素

Baseline 廣泛可用 *

此特性已穩定,併兼容多種裝置和瀏覽器版本。自 ⁨2015 年 11 月⁩起,所有瀏覽器均已支援此特性。

* 此特性的某些部分可能存在不同級別的支援。

<template> HTML 元素作為一種機制,用於儲存 HTML 片段,這些片段可以透過 JavaScript 在以後使用,或者立即生成到 shadow DOM 中。

屬性

此元素包含全域性屬性

shadowrootmode

為父元素建立一個 shadow root。它是 Element.attachShadow() 方法的宣告式版本,並接受相同的 列舉 值。

open

為 JavaScript 暴露內部 shadow root DOM(推薦用於大多數用例)。

closed

向 JavaScript 隱藏內部 shadow root DOM。

注意:HTML 解析器會為具有此屬性並設定為允許值的節點中的第一個 <template> 在 DOM 中建立一個 ShadowRoot 物件。如果未設定此屬性,或者未設定為允許值,或者已經在同一個父節點中宣告式地建立了 ShadowRoot,則會構造一個 HTMLTemplateElement。解析後,HTMLTemplateElement 不能隨後更改為 shadow root,例如透過設定 HTMLTemplateElement.shadowRootMode

注意:你可能會在舊教程和示例中發現非標準的 shadowroot 屬性,該屬性曾受 Chrome 90-110 支援。此屬性已刪除,並被標準 shadowrootmode 屬性取代。

shadowrootclonable

將使用此元素建立的 ShadowRootclonable 屬性值設定為 true。如果設定,使用 Node.cloneNode()Document.importNode() 建立的 shadow host(此 <template> 的父元素)的克隆將包含一個 shadow root。

shadowrootdelegatesfocus

將使用此元素建立的 ShadowRootdelegatesFocus 屬性值設定為 true。如果設定此值,並且選中了 shadow tree 中不可聚焦的元素,則焦點會委託給 tree 中的第一個可聚焦元素。預設值為 false

shadowrootserializable

將使用此元素建立的 ShadowRootserializable 屬性值設定為 true。如果設定,可以透過呼叫 Element.getHTML()ShadowRoot.getHTML() 方法並將 options.serializableShadowRoots 引數設定為 true 來序列化 shadow root。預設值為 false

用法說明

此元素沒有允許的內容,因為 HTML 源中巢狀在其中的所有內容實際上都不會成為 <template> 元素的子級。<template> 元素的 Node.childNodes 屬性始終為空,並且只能透過特殊的 content 屬性訪問這些巢狀內容。但是,如果在 <template> 元素上呼叫 Node.appendChild() 或類似方法,那麼你將把子級插入到 <template> 元素本身中,這違反了其內容模型,並且實際上不會更新 content 屬性返回的 DocumentFragment

由於 <template> 元素的解析方式,模板中所有 <html><head><body> 的開始和結束標籤都是語法錯誤,並且會被解析器忽略,因此 <template><head><title>Test</title></head></template><template><title>Test</title></template> 相同。

使用 <template> 元素主要有兩種方式。

模板文件片段

預設情況下,元素的 content 不會被渲染。相應的 HTMLTemplateElement 介面包含一個標準的 content 屬性(沒有等效的 content/markup 屬性)。此 content 屬性是隻讀的,幷包含一個 DocumentFragment,其中包含模板所表示的 DOM 子樹。此片段可以透過 cloneNode 方法克隆並插入到 DOM 中。

使用 content 屬性時要小心,因為返回的 DocumentFragment 可能會出現意外行為。有關更多詳細資訊,請參閱下面的避免 DocumentFragment 陷阱部分。

宣告式 Shadow DOM

如果 <template> 元素包含 shadowrootmode 屬性,其值為 openclosed,HTML 解析器將立即生成一個 shadow DOM。該元素在 DOM 中被替換為其內容,這些內容被包裝在 ShadowRoot 中,並附加到父元素。這等同於呼叫 Element.attachShadow() 將 shadow root 附加到元素。

如果元素對於 shadowrootmode 具有任何其他值,或者沒有 shadowrootmode 屬性,解析器將生成一個 HTMLTemplateElement。同樣,如果存在多個宣告式 shadow root,則只有第一個會被 ShadowRoot 替換 — 後續例項將解析為 HTMLTemplateElement 物件。

示例

生成表格行

我們首先從示例的 HTML 部分開始。

html
<table id="producttable">
  <thead>
    <tr>
      <td>UPC_Code</td>
      <td>Product_Name</td>
    </tr>
  </thead>
  <tbody>
    <!-- existing data could optionally be included here -->
  </tbody>
</table>

<template id="productrow">
  <tr>
    <td class="record"></td>
    <td></td>
  </tr>
</template>

首先,我們有一個表格,稍後將使用 JavaScript 程式碼將內容插入其中。然後是模板,它描述了代表單個表格行的 HTML 片段的結構。

現在表格已建立,模板已定義,我們使用 JavaScript 將行插入表格中,每行都以模板為基礎進行構造。

js
// Test to see if the browser supports the HTML template element by checking
// for the presence of the template element's content attribute.
if ("content" in document.createElement("template")) {
  // Instantiate the table with the existing HTML tbody
  // and the row with the template
  const tbody = document.querySelector("tbody");
  const template = document.querySelector("#productrow");

  // Clone the new row and insert it into the table
  const clone = template.content.cloneNode(true);
  let td = clone.querySelectorAll("td");
  td[0].textContent = "1235646565";
  td[1].textContent = "Stuff";

  tbody.appendChild(clone);

  // Clone the new row and insert it into the table
  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 {
  // Find another way to add the rows to the table because
  // the HTML template element is not supported.
}

結果是原始的 HTML 表格,並透過 JavaScript 追加了兩行新行

實現宣告式 Shadow DOM

在此示例中,標記的開頭包含一個隱藏的支援警告。如果瀏覽器不支援 shadowrootmode 屬性,則稍後將透過 JavaScript 設定此警告以顯示。接下來,有兩個 <article> 元素,每個都包含巢狀的 <style> 元素,具有不同的行為。第一個 <style> 元素對整個文件是全域性的。第二個元素的作用域是為 <template> 元素生成的 shadow root,因為存在 shadowrootmode 屬性。

html
<p hidden>
  ⛔ Your browser doesn't support <code>shadowrootmode</code> attribute yet.
</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>
js
const isShadowRootModeSupported = Object.hasOwn(
  HTMLTemplateElement.prototype,
  "shadowRootMode",
);

document
  .querySelector("p[hidden]")
  .toggleAttribute("hidden", isShadowRootModeSupported);

具有委託焦點的宣告式 Shadow DOM

此示例演示了 shadowrootdelegatesfocus 如何應用於宣告式建立的 shadow root,以及它對焦點的影響。

該程式碼首先使用帶有 shadowrootmode 屬性的 <template> 元素在 <div> 元素內部宣告一個 shadow root。這會顯示一個包含文字的不可聚焦的 <div> 和一個可聚焦的 <input> 元素。它還使用 CSS 將帶有 :focus 的元素設定為藍色,並設定主機元素的正常樣式。

html
<div>
  <template shadowrootmode="open">
    <style>
      :host {
        display: block;
        border: 1px dotted black;
        padding: 10px;
        margin: 10px;
      }
      :focus {
        outline: 2px solid blue;
      }
    </style>
    <div>Clickable Shadow DOM text</div>
    <input type="text" placeholder="Input inside Shadow DOM" />
  </template>
</div>

第二個程式碼塊除了設定了 shadowrootdelegatesfocus 屬性之外,其他都相同,該屬性將焦點委託給樹中的第一個可聚焦元素,如果選擇了樹中不可聚焦的元素。

html
<div>
  <template shadowrootmode="open" shadowrootdelegatesfocus>
    <style>
      :host {
        display: block;
        border: 1px dotted black;
        padding: 10px;
        margin: 10px;
      }
      :focus {
        outline: 2px solid blue;
      }
    </style>
    <div>Clickable Shadow DOM text</div>
    <input type="text" placeholder="Input inside Shadow DOM" />
  </template>
</div>

最後,我們使用以下 CSS 在父 <div> 元素獲得焦點時應用紅色邊框。

css
div:focus {
  border: 2px solid red;
}

結果如下所示。當 HTML 首次渲染時,元素沒有樣式,如第一張圖片所示。對於未設定 shadowrootdelegatesfocus 的 shadow root,你可以在除了 <input> 之外的任何地方單擊,焦點不會改變(如果選擇 <input> 元素,它將看起來像第二張圖片)。

Screenshot of code with no focus set

對於設定了 shadowrootdelegatesfocus 的 shadow root,單擊文字(不可聚焦)會選擇 <input> 元素,因為這是樹中第一個可聚焦的元素。這也會使父元素獲得焦點,如下所示。

Screenshot of the code where the element has focus

避免 DocumentFragment 陷阱

當傳入 DocumentFragment 值時,Node.appendChild 和類似方法只會將該值的子節點移動到目標節點中。因此,通常最好將事件處理程式附加到 DocumentFragment 的子節點上,而不是附加到 DocumentFragment 本身。

考慮以下 HTML 和 JavaScript

HTML

html
<div id="container"></div>

<template id="template">
  <div>Click me</div>
</template>

JavaScript

js
const container = document.getElementById("container");
const template = document.getElementById("template");

function clickHandler(event) {
  event.target.append(" — Clicked this 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

規範

規範
HTML
# the-template-element

瀏覽器相容性

另見