<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 值。

open

公开 JavaScript 的内部影子根 DOM(推荐用于大多数用例)。

closed

对 JavaScript 隐藏内部影子根 DOM。

注意:这是 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 standard shadowrootmode attribute.

使用说明

¥Usage notes

使用 <template> 元素主要有两种方法:

¥There are two main ways to use the <template> element:

  1. 默认情况下,元素的内容不会被渲染,只会解析为 文档片段。使用 JavaScript 中的 content 属性,可以通过 cloneNode 方法克隆该片段并将其插入到 DOM 中。
  2. 如果元素包含 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.

示例

¥Examples

生成表行

¥Generating table rows

首先我们从示例的 HTML 部分开始。

¥First we start with the HTML portion of the example.

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 片段的结构。

¥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.

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 添加了两个新行:

¥The result is the original HTML table, with two new rows appended to it via JavaScript:

css
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.

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);

避免 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

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);

结果

¥Result

由于 firstCloneDocumentFragment,所以当调用 appendChild 时,只有它的子级被添加到 containerfirstClone 的事件处理程序不会被复制。相反,由于在 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.

技术总结

¥Technical summary

内容类别 元数据内容, 流动内容, 措辞内容, 脚本支持元素
允许的内容 无限制
标签遗漏 无,开始和结束标记都是强制性的。
允许的父级 任何接受 元数据内容措辞内容脚本支持元素 的元素。也允许作为不具有 span 属性的 <colgroup> 元素的子元素。
隐式 ARIA 角色 没有对应的角色
允许的 ARIA 角色 不允许 role
DOM 接口 HTMLTemplateElement

规范

Specification
HTML Standard
# the-template-element

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看

¥See also