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

open

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

closed

对 JavaScript 隐藏内部影子根 DOM。

注意:HTML 解析器在 DOM 中为节点中的第一个 <template> 创建 ShadowRoot 对象,并将此属性设置为允许值。如果未设置属性,或者未将其设置为允许的值 — 或者如果已在同一父级中声明性地创建了 ShadowRoot — 则构造 HTMLTemplateElement。A HTMLTemplateElement 不能在解析后随后更改为影子根,例如,通过设置 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 a ShadowRoot has already been declaratively created in the same parent — then an HTMLTemplateElement is constructed. A HTMLTemplateElement cannot subsequently be changed into a shadow root after parsing, for example, by setting HTMLTemplateElement.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 standard shadowrootmode attribute.

shadowrootclonable

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

shadowrootdelegatesfocus

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

shadowrootserializable Experimental

将使用此元素创建的 ShadowRootserializable 属性的值设置为 true。如果设置,可以通过调用 Element.getHTML()ShadowRoot.getHTML() 方法来序列化影子根,并将 options.serializableShadowRoots 参数设置为 true。值默认为 false

使用说明

¥Usage notes

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

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

模板文档片段

¥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> 元素包含值为 openclosedshadowrootmode 属性,则 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.

示例

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

具有委托焦点的声明式 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.

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 属性,如果选择了树中不可聚焦的元素,则将焦点委托给树中第一个可聚焦元素。

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

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> 元素获得焦点时为其应用绿黄色边框。

¥Last of all we use the following CSS to apply a green-yellow border to the parent <div> element when it has focus.

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

Screenshot of code with no focus set

对于设置了 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.

Screenshot of the code where the element has focus

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