<template>:内容模板元素
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since November 2015.
<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 解析器在 DOM 中为节点中的第一个
<template>
创建ShadowRoot
对象,并将此属性设置为允许值。如果未设置属性,或者未将其设置为允许的值 — 或者如果已在同一父级中声明性地创建了ShadowRoot
— 则构造HTMLTemplateElement
。AHTMLTemplateElement
不能在解析后随后更改为影子根,例如,通过设置HTMLTemplateElement.shadowRootMode
。¥Note: The HTML parser creates a
ShadowRoot
object in the DOM for the first<template>
in a node with this attribute set to an allowed value. If the attribute is not set, or not set to an allowed value — or if aShadowRoot
has already been declaratively created in the same parent — then anHTMLTemplateElement
is constructed. AHTMLTemplateElement
cannot subsequently be changed into a shadow root after parsing, for example, by settingHTMLTemplateElement.shadowRootMode
.注意:你可能会在 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. shadowrootclonable
-
将使用此元素创建的
ShadowRoot
的clonable
属性的值设置为true
。如果设置,则使用Node.cloneNode()
或Document.importNode()
创建的影子主机(此<template>
的父元素)的克隆将在副本中包含影子根。 shadowrootdelegatesfocus
-
将使用此元素创建的
ShadowRoot
的delegatesFocus
属性的值设置为true
。如果设置了此选项并且选择了影子树中的不可聚焦元素,则焦点将委托给树中第一个可聚焦元素。值默认为false
。 shadowrootserializable
Experimental-
将使用此元素创建的
ShadowRoot
的serializable
属性的值设置为true
。如果设置,可以通过调用Element.getHTML()
或ShadowRoot.getHTML()
方法来序列化影子根,并将options.serializableShadowRoots
参数设置为true
。值默认为false
。
使用说明
模板文档片段
¥Template document fragment
默认情况下,不呈现元素的内容。相应的 HTMLTemplateElement
接口包括标准 content
属性(没有等效的内容/标记属性)。此 content
属性是只读的,并保存包含模板表示的 DOM 子树的 DocumentFragment
。此片段可以通过 cloneNode
方法克隆并插入 DOM。
¥By default, the element's content is not rendered.
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.
This fragment can be cloned via the cloneNode
method and inserted into the DOM.
使用 content
属性时要小心,因为返回的 DocumentFragment
可能会出现意外行为。有关更多详细信息,请参阅下面的 避免 DocumentFragment 陷阱 部分。
¥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.
声明式 Shadow DOM
¥Declarative Shadow DOM
如果 <template>
元素包含值为 open
或 closed
的 shadowrootmode
属性,则 HTML 解析器将立即生成影子 DOM。元素在 DOM 中被其内容替换,该内容封装在 ShadowRoot
中,并附加到父元素。这相当于调用 Element.attachShadow()
将影子根附加到元素的声明性等同物。
¥If the <template>
element contains the shadowrootmode
attribute with a value of either open
or closed
, the HTML parser will immediately generate a shadow DOM. The element is replaced in the DOM by its content wrapped in a ShadowRoot
, which is attached to the parent element.
This is the declarative equivalent of calling Element.attachShadow()
to attach a shadow root to an element.
如果元素的 shadowrootmode
有任何其他值,或者没有 shadowrootmode
属性,则解析器会生成 HTMLTemplateElement
。类似地,如果有多个声明性影子根,则只有第一个被 ShadowRoot
替换 - 后续实例被解析为 HTMLTemplateElement
对象。
¥If the element has any other value for shadowrootmode
, or does not have the shadowrootmode
attribute, the parser generates a HTMLTemplateElement
.
Similarly, if there are multiple declarative shadow roots, only the first one is replaced by a ShadowRoot
— subsequent instances are parsed as HTMLTemplateElement
objects.
示例
生成表行
¥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);
具有委托焦点的声明式 Shadow DOM
¥Declarative Shadow DOM with delegated focus
此示例演示了如何将 shadowrootdelegatesfocus
应用于声明式创建的影子根,以及这对焦点的影响。
¥This example demonstrates how shadowrootdelegatesfocus
is applied to a shadow root that is created declaratively, and the effect this has on focus.
代码首先在 <div>
元素内声明一个影子根,使用带有 shadowrootmode
属性的 <template>
元素。这将显示一个包含文本的不可聚焦的 <div>
和一个可聚焦的 <input>
元素。它还使用 CSS 将具有 :focus
的元素样式设置为蓝色,并设置宿主元素的正常样式。
¥The code first declares a shadow root inside a <div>
element, using the <template>
element with the shadowrootmode
attribute.
This displays both a non-focusable <div>
containing text and a focusable <input>
element.
It also uses CSS to style elements with :focus
to blue, and to set the normal styling of the host element.
<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
属性,如果选择了树中不可聚焦的元素,则将焦点委托给树中第一个可聚焦元素。
¥The second code block is identical except that it sets the shadowrootdelegatesfocus
attribute, which delegates focus to the first focusable element in the tree if a non-focusable element in the tree is selected.
<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>
元素获得焦点时为其应用绿黄色边框。
¥Last of all we use the following CSS to apply a green-yellow border to the parent <div>
element when it has focus.
div:focus {
border: 2px solid red;
}
结果如下所示。首次呈现 HTML 时,元素没有样式,如第一张图所示。对于未设置 shadowrootdelegatesfocus
的影子根,你可以单击除 <input>
之外的任何地方,焦点不会改变(如果选择 <input>
元素,它将看起来像第二张图片)。
¥The results are shown below.
When the HTML is first rendered, the elements have no styling, as shown in the first image.
For the shadow root that does not have shadowrootdelegatesfocus
set you can click anywhere except the <input>
and the focus does not change (if you select the <input>
element it will look like the second image).
对于设置了 shadowrootdelegatesfocus
的影子根,单击文本(不可聚焦)会选择 <input>
元素,因为这是树中第一个可聚焦元素。这也关注父元素,如下所示。
¥For the shadow root with shadowrootdelegatesfocus
set, clicking on the text (which is non-focusable) selects the <input>
element, as this is the first focusable element in the tree.
This also focusses the parent element as shown below.
避免 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 范围 模块
- 使用 Shadow DOM 中的 声明式 Shadow DOM(带有 html)
- developer.chrome.com 上的 声明式影子 DOM (2023)