<template>: 内容模板元素

基线 广泛可用

此功能已建立良好,并在许多设备和浏览器版本上都能正常工作。它自 2015 年 11 月.

<template> HTML 元素充当用于保存 HTML 片段的机制,这些片段可以稍后通过 JavaScript 使用,也可以立即生成到影子 DOM 中。

属性

此元素包含 全局属性

shadowrootmode

为父元素创建 影子根。它是 Element.attachShadow() 方法的声明式版本,并接受相同的 枚举 值。

open

为 JavaScript 公开内部影子根 DOM(建议用于大多数用例)。

closed

将内部影子根 DOM 隐藏在 JavaScript 之外。

注意:HTML 解析器在 DOM 中为第一个 <template> 创建一个 ShadowRoot 对象,该对象具有设置为允许值的属性。如果属性未设置或未设置为允许值 - 或者如果在同一个父节点中已声明式创建了 ShadowRoot,则将构建一个 HTMLTemplateElement。解析后,HTMLTemplateElement 无法随后更改为影子根,例如,通过设置 HTMLTemplateElement.shadowRootMode

注意:你可能会在旧的教程和示例中找到非标准的 shadowroot 属性,这些属性过去曾在 Chrome 90-110 中得到支持。此属性已被删除,并由标准 shadowrootmode 属性取代。

shadowrootclonable

将使用此元素创建的 ShadowRootclonable 属性值设置为 true。如果设置,则使用 Node.cloneNode()Document.importNode() 创建的影子主机(此 <template> 的父元素)的克隆将包含副本中的影子根。

shadowrootdelegatesfocus

将使用此元素创建的 ShadowRootdelegatesFocus 属性值设置为 true。如果设置了此值,并且选择了影子树中的不可聚焦元素,则焦点将委托给树中的第一个可聚焦元素。默认值为 false

shadowrootserializable 实验性

将使用此元素创建的 ShadowRootserializable 属性值设置为 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 部分开始。

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 向其追加了两行。

实现声明式影子 DOM

在本例中,在标记的开头包含了一个隐藏的支持警告。如果浏览器不支持shadowrootmode属性,则该警告将在稍后通过 JavaScript 设置为显示。接下来,有两个<article>元素,每个元素都包含嵌套的<style>元素,它们的行为不同。第一个<style>元素对整个文档是全局的。第二个元素被限定在<template>元素所在位置生成的影子根中,因为存在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 =
  HTMLTemplateElement.prototype.hasOwnProperty("shadowRootMode");

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

具有委托焦点的声明式影子 DOM

此示例演示了如何将shadowrootdelegatesfocus应用于声明式创建的影子根,以及这种操作对焦点的影响。

代码首先使用带有shadowrootmode属性的<template>元素,在<div>元素内声明一个影子根。这将显示一个不可聚焦的包含文本的<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的影子根,您可以点击除<input>以外的任何地方,焦点都不会改变(如果选择<input>元素,它看起来就像第二张图片)。

Screenshot of code with no focus set

对于设置了shadowrootdelegatesfocus的影子根,点击文本(不可聚焦)会选择<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时事件处理程序会被复制,点击它会像预期的那样工作。

技术摘要

内容类别 元数据内容流内容短语内容脚本支持元素
允许的内容 无限制
标签省略 没有,起始标签和结束标签都是必需的。
允许的父元素 任何接受元数据内容短语内容脚本支持元素的元素。还允许作为<colgroup>元素的子元素,该元素没有span属性。
隐式 ARIA 角色 没有相应的角色
允许的 ARIA 角色 不允许使用role
DOM 接口 HTMLTemplateElement

规范

规范
HTML 标准
# the-template-element

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参见