<template>: The Content Template element
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.
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
-
Creates a shadow root for the parent element. It is a declarative version of the
Element.attachShadow()
method and accepts the same enumerated values.open
-
Exposes the internal shadow root DOM for JavaScript (recommended for most use cases).
closed
-
Hides the internal shadow root DOM from JavaScript.
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.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.
Usage notes
There are two main ways to use the <template>
element:
- By default, the element's content is not rendered, only parsed into a document fragment. Using the
content
property in JavaScript, this fragment can be cloned via thecloneNode
method and inserted into the DOM. - If the element contains the
shadowrootmode
attribute, the HTML parser will immediately generate a shadow DOM. The element is replaced in the DOM by its content wrapped in a shadow root.
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
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>
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.
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.
}
The result is the original HTML table, with two new rows appended to it via JavaScript:
Implementing a declarative shadow DOM
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);
Avoiding DocumentFragment pitfalls
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.
Consider the following HTML and 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);
Result
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
Content categories | Metadata content, flow content, phrasing content, script-supporting element |
---|---|
Permitted content | No restrictions |
Tag omission | None, both the starting and ending tag are mandatory. |
Permitted parents |
Any element that accepts
metadata content,
phrasing content, or
script-supporting elements. Also allowed as a child of a <colgroup>
element that does not have a
span attribute.
|
Implicit ARIA role | No corresponding role |
Permitted ARIA roles | No role permitted |
DOM interface | HTMLTemplateElement |
Specifications
Specification |
---|
HTML Standard # the-template-element |
Browser compatibility
BCD tables only load in the browser
See also
part
andexportparts
HTML attributes<slot>
HTML element:host
,:host()
, and:host-context()
CSS pseudo-classes::part
and::slotted
CSS pseudo-elementsShadowRoot
interface- Using templates and slots
- CSS scoping module
- Declarative shadow DOM on developer.chrome.com (2023)