<template>: 內容模板元素
Baseline 廣泛可用 *
<template> HTML 元素作為一種機制,用於儲存 HTML 片段,這些片段可以透過 JavaScript 在以後使用,或者立即生成到 shadow DOM 中。
屬性
此元素包含全域性屬性。
shadowrootmode-
為父元素建立一個 shadow root。它是
Element.attachShadow()方法的宣告式版本,並接受相同的 列舉 值。注意:HTML 解析器會為具有此屬性並設定為允許值的節點中的第一個
<template>在 DOM 中建立一個ShadowRoot物件。如果未設定此屬性,或者未設定為允許值,或者已經在同一個父節點中宣告式地建立了ShadowRoot,則會構造一個HTMLTemplateElement。解析後,HTMLTemplateElement不能隨後更改為 shadow root,例如透過設定HTMLTemplateElement.shadowRootMode。注意:你可能會在舊教程和示例中發現非標準的
shadowroot屬性,該屬性曾受 Chrome 90-110 支援。此屬性已刪除,並被標準shadowrootmode屬性取代。 shadowrootclonable-
將使用此元素建立的
ShadowRoot的clonable屬性值設定為true。如果設定,使用Node.cloneNode()或Document.importNode()建立的 shadow host(此<template>的父元素)的克隆將包含一個 shadow root。 shadowrootdelegatesfocus-
將使用此元素建立的
ShadowRoot的delegatesFocus屬性值設定為true。如果設定此值,並且選中了 shadow tree 中不可聚焦的元素,則焦點會委託給 tree 中的第一個可聚焦元素。預設值為false。 shadowrootserializable-
將使用此元素建立的
ShadowRoot的serializable屬性值設定為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 屬性,其值為 open 或 closed,HTML 解析器將立即生成一個 shadow DOM。該元素在 DOM 中被替換為其內容,這些內容被包裝在 ShadowRoot 中,並附加到父元素。這等同於呼叫 Element.attachShadow() 將 shadow root 附加到元素。
如果元素對於 shadowrootmode 具有任何其他值,或者沒有 shadowrootmode 屬性,解析器將生成一個 HTMLTemplateElement。同樣,如果存在多個宣告式 shadow root,則只有第一個會被 ShadowRoot 替換 — 後續例項將解析為 HTMLTemplateElement 物件。
示例
生成表格行
我們首先從示例的 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 將行插入表格中,每行都以模板為基礎進行構造。
// 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 屬性。
<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>
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 的元素設定為藍色,並設定主機元素的正常樣式。
<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 屬性之外,其他都相同,該屬性將焦點委託給樹中的第一個可聚焦元素,如果選擇了樹中不可聚焦的元素。
<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> 元素獲得焦點時應用紅色邊框。
div:focus {
border: 2px solid red;
}
結果如下所示。當 HTML 首次渲染時,元素沒有樣式,如第一張圖片所示。對於未設定 shadowrootdelegatesfocus 的 shadow root,你可以在除了 <input> 之外的任何地方單擊,焦點不會改變(如果選擇 <input> 元素,它將看起來像第二張圖片)。

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

避免 DocumentFragment 陷阱
當傳入 DocumentFragment 值時,Node.appendChild 和類似方法只會將該值的子節點移動到目標節點中。因此,通常最好將事件處理程式附加到 DocumentFragment 的子節點上,而不是附加到 DocumentFragment 本身。
考慮以下 HTML 和 JavaScript
HTML
<div id="container"></div>
<template id="template">
<div>Click me</div>
</template>
JavaScript
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 時事件處理程式會被複制,並且單擊它會像預期一樣工作。
技術摘要
規範
| 規範 |
|---|
| HTML # the-template-element |
瀏覽器相容性
載入中…
另見
part和exportpartsHTML 屬性<slot>HTML 元素:has-slotted、:host、:host()和:host-context()CSS 偽類::part和::slottedCSS 偽元素ShadowRoot介面- 使用模板和插槽
- CSS 作用域模組
- 在《使用 Shadow DOM》中使用 HTML 宣告式 Shadow DOM
- web.dev 上關於宣告式 Shadow DOM 的文章 (2023)