<template>:内容模板元素
<template>
HTML 元素作为保存 HTML 片段的机制,可以稍后通过 JavaScript 使用或立即生成到影子 DOM 中。
¥The <template>
HTML element serves as a mechanism for holding HTML fragments, which can either be used later via JavaScript or generated immediately into shadow DOM.
属性
¥Attributes
该元素包括 全局属性。
¥This element includes the global attributes.
shadowrootmode
-
为父元素创建 影子根。它是
Element.attachShadow()
方法的声明式版本,接受相同的 enumerated 值。注意:这是 HTML 解析器的一个功能,无法通过 JavaScript 设置
shadowrootmode
属性来进行解析后使用。只有允许的值才会创建影子根;任何其他值(包括空值)都不会触发此行为。¥Note: This is a feature of the HTML parser that cannot be used post-parsing by setting the
shadowrootmode
attribute through JavaScript. Only allowed values will create the shadow root; any other values, including empty ones, won't trigger this behavior.注意:你可能会在 Chrome 90-110 曾经支持的旧教程和示例中找到非标准
shadowroot
属性。此属性已被删除并由标准shadowrootmode
属性取代。¥Note: You may find the non-standard
shadowroot
attribute in older tutorials and examples that used to be supported in Chrome 90-110. This attribute has since been removed and replaced by the standardshadowrootmode
attribute.
使用说明
¥Usage notes
使用 <template>
元素主要有两种方法:
¥There are two main ways to use the <template>
element:
- 默认情况下,元素的内容不会被渲染,只会解析为 文档片段。使用 JavaScript 中的
content
属性,可以通过cloneNode
方法克隆该片段并将其插入到 DOM 中。 - 如果元素包含
shadowrootmode
属性,HTML 解析器将立即生成影子 DOM。该元素在 DOM 中被封装在 影子根 中的内容替换。
相应的 HTMLTemplateElement
接口包括标准 content
属性(没有等效的内容/标记属性)。此 content
属性是只读的,并保存包含模板表示的 DOM 子树的 DocumentFragment
。使用 content
属性时要小心,因为返回的 DocumentFragment
可能会出现意外行为。有关更多详细信息,请参阅下面的 避免 DocumentFragment 陷阱 部分。
¥The corresponding HTMLTemplateElement
interface includes a standard content
property (without an equivalent content/markup attribute). This content
property is read-only and holds a DocumentFragment
that contains the DOM subtree represented by the template. Be careful when using the content
property because the returned DocumentFragment
can exhibit unexpected behavior. For more details, see the Avoiding DocumentFragment pitfalls section below.
示例
生成表行
¥Generating table rows
首先我们从示例的 HTML 部分开始。
¥First we start with the HTML portion of the example.
<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 片段的结构。
¥First, we have a table into which we will later insert content using JavaScript code. Then comes the template, which describes the structure of an HTML fragment representing a single table row.
现在已经创建了表并定义了模板,我们使用 JavaScript 将行插入表中,每行都是使用模板作为其基础来构造的。
¥Now that the table has been created and the template defined, we use JavaScript to insert rows into the table, with each row being constructed using the template as its basis.
// 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 添加了两个新行:
¥The result is the original HTML table, with two new rows appended to it via JavaScript:
table {
background: #000;
}
table td {
background: #fff;
}
实现声明式影子 DOM
¥Implementing a declarative shadow DOM
在此示例中,标记的开头包含隐藏的支持警告。如果浏览器不支持 shadowrootmode
属性,此警告稍后将通过 JavaScript 显示。接下来,有两个 <article>
元素,每个元素包含具有不同行为的嵌套 <style>
元素。第一个 <style>
元素对于整个文档来说是全局的。由于 shadowrootmode
属性的存在,第二个的作用域是代替 <template>
元素生成的影子根。
¥In this example, a hidden support warning is included at the beginning of the markup. This warning is later set to be displayed via JavaScript if the browser doesn't support the shadowrootmode
attribute. Next, there are two <article>
elements, each containing nested <style>
elements with different behaviors. The first <style>
element is global to the whole document. The second one is scoped to the shadow root generated in place of the <template>
element because of the presence of the shadowrootmode
attribute.
<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 =
HTMLTemplateElement.prototype.hasOwnProperty("shadowRootMode");
document
.querySelector("p[hidden]")
.toggleAttribute("hidden", isShadowRootModeSupported);
避免 DocumentFragment 陷阱
¥Avoiding DocumentFragment pitfalls
当传递 DocumentFragment
值时,Node.appendChild
和类似方法仅将该值的子节点移动到目标节点。因此,通常最好将事件处理程序附加到 DocumentFragment
的子级,而不是附加到 DocumentFragment
本身。
¥When a DocumentFragment
value is passed, Node.appendChild
and similar methods move only the child nodes of that value into the target node. Therefore, it is usually preferable to attach event handlers to the children of a DocumentFragment
, rather than to the DocumentFragment
itself.
考虑以下 HTML 和 JavaScript:
¥Consider the following HTML and JavaScript:
HTML
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);
结果
¥Result
由于 firstClone
是 DocumentFragment
,所以当调用 appendChild
时,只有它的子级被添加到 container
;firstClone
的事件处理程序不会被复制。相反,由于在 secondClone
的第一个子节点上添加了事件处理程序,因此当调用 appendChild
时,事件处理程序会被复制,并且单击它会正常工作。
¥Since firstClone
is a DocumentFragment
, only its children are added to container
when appendChild
is called; the event handlers of firstClone
are not copied. In contrast, because an event handler is added to the first child node of secondClone
, the event handler is copied when appendChild
is called, and clicking on it works as one would expect.
技术总结
规范
Specification |
---|
HTML Standard # the-template-element |
浏览器兼容性
BCD tables only load in the browser
也可以看看
¥See also
part
和exportparts
HTML 属性<slot>
HTML 元素:host
、:host()
和:host-context()
CSS 伪类::part
和::slotted
CSS 伪元素ShadowRoot
接口- 使用模板和插槽
- CSS 范围 模块
- developer.chrome.com 上的 声明式影子 DOM (2023)