<template>: 内容模板元素
基线 广泛可用
此功能已建立良好,并在许多设备和浏览器版本上都能正常工作。它自 2015 年 11 月.
属性
此元素包含 全局属性。
shadowrootmode
-
为父元素创建 影子根。它是
Element.attachShadow()
方法的声明式版本,并接受相同的 枚举 值。注意:HTML 解析器在 DOM 中为第一个
<template>
创建一个ShadowRoot
对象,该对象具有设置为允许值的属性。如果属性未设置或未设置为允许值 - 或者如果在同一个父节点中已声明式创建了ShadowRoot
,则将构建一个HTMLTemplateElement
。解析后,HTMLTemplateElement
无法随后更改为影子根,例如,通过设置HTMLTemplateElement.shadowRootMode
。注意:你可能会在旧的教程和示例中找到非标准的
shadowroot
属性,这些属性过去曾在 Chrome 90-110 中得到支持。此属性已被删除,并由标准shadowrootmode
属性取代。 shadowrootclonable
-
将使用此元素创建的
ShadowRoot
的clonable
属性值设置为true
。如果设置,则使用Node.cloneNode()
或Document.importNode()
创建的影子主机(此<template>
的父元素)的克隆将包含副本中的影子根。 shadowrootdelegatesfocus
-
将使用此元素创建的
ShadowRoot
的delegatesFocus
属性值设置为true
。如果设置了此值,并且选择了影子树中的不可聚焦元素,则焦点将委托给树中的第一个可聚焦元素。默认值为false
。 shadowrootserializable
实验性-
将使用此元素创建的
ShadowRoot
的serializable
属性值设置为true
。如果设置,则可以通过调用Element.getHTML()
或ShadowRoot.getHTML()
方法(将options.serializableShadowRoots
参数设置为true
)序列化影子根。默认值为false
。
使用说明
使用 <template>
元素主要有两种方法。
模板文档片段
默认情况下,元素的内容不会被渲染。相应的 HTMLTemplateElement
接口包含一个标准的 content
属性(没有等效的 content/markup 属性)。此 content
属性是只读的,它保存一个 DocumentFragment
,该片段包含由模板表示的 DOM 子树。此片段可以通过 cloneNode
方法克隆,并插入到 DOM 中。
使用 content
属性时要小心,因为返回的 DocumentFragment
可能表现出意外的行为。有关更多详细信息,请参见下面的 避免 DocumentFragment 陷阱 部分。
声明式影子 DOM
如果 <template>
元素包含 shadowrootmode
属性,
如果元素的shadowrootmode
属性具有任何其他值,或者没有shadowrootmode
属性,解析器会生成一个HTMLTemplateElement
。类似地,如果存在多个声明式影子根,则只有第一个被替换为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 向其追加了两行。
实现声明式影子 DOM
在本例中,在标记的开头包含了一个隐藏的支持警告。如果浏览器不支持shadowrootmode
属性,则该警告将在稍后通过 JavaScript 设置为显示。接下来,有两个<article>
元素,每个元素都包含嵌套的<style>
元素,它们的行为不同。第一个<style>
元素对整个文档是全局的。第二个元素被限定在<template>
元素所在位置生成的影子根中,因为存在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 =
HTMLTemplateElement.prototype.hasOwnProperty("shadowRootMode");
document
.querySelector("p[hidden]")
.toggleAttribute("hidden", isShadowRootModeSupported);
具有委托焦点的声明式影子 DOM
此示例演示了如何将shadowrootdelegatesfocus
应用于声明式创建的影子根,以及这种操作对焦点的影响。
代码首先使用带有shadowrootmode
属性的<template>
元素,在<div>
元素内声明一个影子根。这将显示一个不可聚焦的包含文本的<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
的影子根,您可以点击除<input>
以外的任何地方,焦点都不会改变(如果选择<input>
元素,它看起来就像第二张图片)。
对于设置了shadowrootdelegatesfocus
的影子根,点击文本(不可聚焦)会选择<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 |
浏览器兼容性
BCD 表格仅在浏览器中加载
另请参见
part
和exportparts
HTML 属性<slot>
HTML 元素:host
,:host()
和:host-context()
CSS 伪类::part
和::slotted
CSS 伪元素ShadowRoot
接口- 使用模板和插槽
- CSS 限定范围 模块
- 声明式影子 DOM(使用 html) 在使用影子 DOM中
- 声明式影子 DOM 在 web.dev 上(2023 年)