<template>: Phần tử template nội dung
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.
* Some parts of this feature may have varying levels of support.
Phần tử <template> trong HTML đóng vai trò là cơ chế để giữ các đoạn HTML, có thể được dùng sau qua JavaScript hoặc được tạo ngay lập tức vào shadow DOM.
Thuộc tính
Phần tử này bao gồm các thuộc tính toàn cục.
shadowrootmode-
Tạo shadow root cho phần tử cha. Đây là phiên bản khai báo của phương thức
Element.attachShadow()và chấp nhận các giá trị enumerated giống nhau.open-
Hiển thị shadow root DOM nội bộ cho JavaScript (được khuyến nghị cho hầu hết các trường hợp sử dụng).
closed-
Ẩn shadow root DOM nội bộ khỏi JavaScript.
Note: Trình phân tích cú pháp HTML tạo đối tượng
ShadowRoottrong DOM cho<template>đầu tiên trong một node với thuộc tính này được đặt thành giá trị được phép. Nếu thuộc tính không được đặt, hoặc không được đặt thành giá trị được phép — hoặc nếuShadowRootđã được tạo khai báo trong cùng một cha — thì mộtHTMLTemplateElementđược tạo ra. MộtHTMLTemplateElementkhông thể được thay đổi thành shadow root sau khi phân tích cú pháp, ví dụ bằng cách đặtHTMLTemplateElement.shadowRootMode.Note: Bạn có thể tìm thấy thuộc tính không chuẩn
shadowroottrong các hướng dẫn và ví dụ cũ hơn trước đây được hỗ trợ trong Chrome 90-110. Thuộc tính này đã bị xóa và được thay thế bằng thuộc tính tiêu chuẩnshadowrootmode. shadowrootclonable-
Đặt giá trị của thuộc tính
clonablecủaShadowRootđược tạo bằng phần tử này thànhtrue. Nếu được đặt, bản sao của shadow host (phần tử cha của<template>này) được tạo bằngNode.cloneNode()hoặcDocument.importNode()sẽ bao gồm shadow root trong bản sao. shadowrootcustomelementregistry-
Đặt thuộc tính
customElementRegistrycủaShadowRootđược tạo bằng phần tử này thànhnull, thay vì registry custom element của tài liệu. Điều này cho phépCustomElementRegistrycó phạm vi được gắn sau bằngCustomElementRegistry.initialize(). shadowrootdelegatesfocus-
Đặt giá trị của thuộc tính
delegatesFocuscủaShadowRootđược tạo bằng phần tử này thànhtrue. Nếu điều này được đặt và một phần tử không thể lấy tiêu điểm trong cây shadow được chọn, thì tiêu điểm sẽ được ủy quyền cho phần tử đầu tiên có thể lấy tiêu điểm trong cây. Giá trị mặc định làfalse. shadowrootreferencetargetExperimental Non-standard-
Đặt giá trị của thuộc tính
referenceTargetcủaShadowRootđược tạo bằng phần tử này. Giá trị phải là ID của một phần tử bên trong shadow DOM. Nếu được đặt, các tham chiếu mục tiêu đến phần tử host từ bên ngoài shadow DOM sẽ khiến phần tử mục tiêu được tham chiếu trở thành mục tiêu hiệu lực của tham chiếu đến phần tử host. shadowrootserializable-
Đặt giá trị của thuộc tính
serializablecủaShadowRootđược tạo bằng phần tử này thànhtrue. Nếu được đặt, shadow root có thể được tuần tự hóa bằng cách gọi các phương thứcElement.getHTML()hoặcShadowRoot.getHTML()với tham sốoptions.serializableShadowRootsđược đặt làtrue. Giá trị mặc định làfalse.
Ghi chú sử dụng
Phần tử này không có nội dung được phép, vì mọi thứ được lồng bên trong nó trong nguồn HTML thực tế không trở thành con của phần tử <template>. Thuộc tính Node.childNodes của phần tử <template> luôn trống, và bạn chỉ có thể truy cập nội dung được lồng đó qua thuộc tính đặc biệt content. Tuy nhiên, nếu bạn gọi Node.appendChild() hoặc các phương thức tương tự trên phần tử <template>, thì bạn sẽ chèn các con vào chính phần tử <template>, đây là vi phạm mô hình nội dung của nó và không thực sự cập nhật DocumentFragment được trả về bởi thuộc tính content.
Do cách phần tử <template> được phân tích cú pháp, tất cả các thẻ mở và đóng <html>, <head>, và <body> bên trong template là lỗi cú pháp và bị trình phân tích cú pháp bỏ qua, vì vậy <template><head><title>Test</title></head></template> giống như <template><title>Test</title></template>.
Có hai cách chính để dùng phần tử <template>.
Đoạn tài liệu template
Mặc định, nội dung của phần tử không được hiển thị.
Giao diện HTMLTemplateElement tương ứng bao gồm thuộc tính content tiêu chuẩn (không có thuộc tính nội dung/đánh dấu tương đương). Thuộc tính content này là chỉ đọc và giữ DocumentFragment chứa cây con DOM được biểu diễn bởi template.
Các phương thức Node.cloneNode() và Document.importNode() đều tạo bản sao của một node. Sự khác biệt là importNode() sao chép node trong ngữ cảnh của tài liệu gọi, trong khi cloneNode() dùng tài liệu của node đang được sao chép. Ngữ cảnh tài liệu xác định CustomElementRegistry để xây dựng bất kỳ custom element nào. Vì lý do này, hãy dùng document.importNode() để sao chép đoạn content để các hậu duệ custom element được xây dựng bằng các định nghĩa trong tài liệu hiện tại, thay vì tài liệu riêng biệt sở hữu nội dung template. Xem ví dụ trên trang Node.cloneNode() để biết thêm chi tiết.
Lưu ý rằng bản thân container DocumentFragment không nên có dữ liệu đính kèm vào nó. Xem ví dụ Dữ liệu trên DocumentFragment không được sao chép để biết thêm chi tiết.
Shadow DOM khai báo
Nếu phần tử <template> chứa thuộc tính shadowrootmode với giá trị open hoặc closed, trình phân tích cú pháp HTML sẽ tạo ngay lập tức shadow DOM. Phần tử được thay thế trong DOM bởi nội dung của nó được bọc trong ShadowRoot, được gắn vào phần tử cha.
Đây là tương đương khai báo của việc gọi Element.attachShadow() để gắn shadow root vào phần tử.
Nếu phần tử có bất kỳ giá trị nào khác cho shadowrootmode, hoặc không có thuộc tính shadowrootmode, trình phân tích cú pháp tạo HTMLTemplateElement.
Tương tự, nếu có nhiều shadow root khai báo, chỉ cái đầu tiên được thay thế bởi ShadowRoot — các phiên bản tiếp theo được phân tích cú pháp thành các đối tượng HTMLTemplateElement.
Ví dụ
>Tạo hàng bảng
Đầu tiên chúng ta bắt đầu với phần HTML của ví dụ.
<table id="producttable">
<thead>
<tr>
<td>UPC_Code</td>
<td>Product_Name</td>
</tr>
</thead>
<tbody>
<!-- dữ liệu hiện có có thể tùy chọn được bao gồm ở đây -->
</tbody>
</table>
<template id="productrow">
<tr>
<td class="record"></td>
<td></td>
</tr>
</template>
Đầu tiên, chúng ta có bảng mà chúng ta sau đó sẽ chèn nội dung bằng mã JavaScript. Sau đó là template, mô tả cấu trúc của đoạn HTML biểu diễn một hàng bảng duy nhất.
Bây giờ bảng đã được tạo và template được xác định, chúng ta dùng JavaScript để chèn các hàng vào bảng, mỗi hàng được xây dựng bằng template làm cơ sở.
// Kiểm tra xem trình duyệt có hỗ trợ phần tử HTML template bằng cách kiểm tra
// sự hiện diện của thuộc tính content của phần tử template.
if ("content" in document.createElement("template")) {
// Khởi tạo bảng với HTML tbody hiện có
// và hàng với template
const tbody = document.querySelector("tbody");
const template = document.querySelector("#productrow");
// Sao chép hàng mới và chèn vào bảng
const clone = document.importNode(template.content, true);
let td = clone.querySelectorAll("td");
td[0].textContent = "1235646565";
td[1].textContent = "Stuff";
tbody.appendChild(clone);
// Sao chép hàng mới và chèn vào bảng
const clone2 = document.importNode(template.content, true);
td = clone2.querySelectorAll("td");
td[0].textContent = "0384928528";
td[1].textContent = "Acme Kidney Beans 2";
tbody.appendChild(clone2);
} else {
// Tìm cách khác để thêm hàng vào bảng vì
// phần tử HTML template không được hỗ trợ.
}
Kết quả là bảng HTML gốc, với hai hàng mới được thêm vào qua JavaScript:
Triển khai shadow DOM khai báo
Trong ví dụ này, cảnh báo hỗ trợ ẩn được bao gồm ở đầu đánh dấu. Cảnh báo này sau được đặt để hiển thị qua JavaScript nếu trình duyệt không hỗ trợ thuộc tính shadowrootmode. Tiếp theo, có hai phần tử <article>, mỗi phần tử chứa các phần tử <style> lồng nhau với các hành vi khác nhau. Phần tử <style> đầu tiên là toàn cục cho toàn bộ tài liệu. Phần tử thứ hai được giới hạn trong shadow root được tạo ra thay cho phần tử <template> do sự hiện diện của thuộc tính 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 = Object.hasOwn(
HTMLTemplateElement.prototype,
"shadowRootMode",
);
document
.querySelector("p[hidden]")
.toggleAttribute("hidden", isShadowRootModeSupported);
Shadow DOM khai báo với ủy quyền tiêu điểm
Ví dụ này minh họa cách shadowrootdelegatesfocus được áp dụng cho shadow root được tạo khai báo, và ảnh hưởng của nó đến tiêu điểm.
Mã đầu tiên khai báo shadow root bên trong phần tử <div>, bằng cách dùng phần tử <template> với thuộc tính shadowrootmode.
Điều này hiển thị cả <div> không thể lấy tiêu điểm chứa văn bản và phần tử <input> có thể lấy tiêu điểm.
Nó cũng dùng CSS để tạo kiểu các phần tử với :focus thành màu xanh, và đặt kiểu thông thường của phần tử host.
<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>
Khối mã thứ hai giống hệt ngoại trừ việc nó đặt thuộc tính shadowrootdelegatesfocus, ủy quyền tiêu điểm cho phần tử đầu tiên có thể lấy tiêu điểm trong cây nếu một phần tử không thể lấy tiêu điểm trong cây được chọn.
<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>
Cuối cùng chúng ta dùng CSS sau để áp dụng viền đỏ cho phần tử <div> cha khi nó có tiêu điểm.
div:focus {
border: 2px solid red;
}
Kết quả được hiển thị bên dưới.
Khi HTML lần đầu được hiển thị, các phần tử không có kiểu, như được hiển thị trong hình ảnh đầu tiên.
Đối với shadow root không có shadowrootdelegatesfocus được đặt, bạn có thể nhấp vào bất cứ đâu ngoại trừ <input> và tiêu điểm không thay đổi (nếu bạn chọn phần tử <input> nó sẽ trông như hình ảnh thứ hai).

Đối với shadow root với shadowrootdelegatesfocus được đặt, nhấp vào văn bản (không thể lấy tiêu điểm) sẽ chọn phần tử <input>, vì đây là phần tử đầu tiên có thể lấy tiêu điểm trong cây.
Điều này cũng đặt tiêu điểm cho phần tử cha như được hiển thị bên dưới.

Dữ liệu trên DocumentFragment không được sao chép
Khi giá trị DocumentFragment được truyền, Node.appendChild và các phương thức tương tự chỉ di chuyển các node con của giá trị đó vào node đích. Do đó, thường nên đính kèm trình xử lý sự kiện vào các con của DocumentFragment, thay vì chính DocumentFragment.
Hãy xem xét HTML và JavaScript sau:
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 = document.importNode(template.content, true);
firstClone.addEventListener("click", clickHandler);
container.appendChild(firstClone);
const secondClone = document.importNode(template.content, true);
secondClone.children[0].addEventListener("click", clickHandler);
container.appendChild(secondClone);
Kết quả
Vì firstClone là DocumentFragment, chỉ có các con của nó được thêm vào container khi appendChild được gọi; trình xử lý sự kiện của firstClone không được sao chép. Ngược lại, vì trình xử lý sự kiện được thêm vào node con đầu tiên của secondClone, trình xử lý sự kiện được sao chép khi appendChild được gọi, và nhấp vào nó hoạt động như mong đợi.
Tóm tắt kỹ thuật
| Danh mục nội dung | Nội dung metadata, nội dung luồng, nội dung diễn đạt, phần tử hỗ trợ script |
|---|---|
| Nội dung được phép | Không có (xem Ghi chú sử dụng) |
| Bỏ thẻ | Không, cả thẻ mở và thẻ đóng đều bắt buộc. |
| Phần tử cha được phép |
Bất kỳ phần tử nào chấp nhận
nội dung metadata,
nội dung diễn đạt, hoặc
phần tử hỗ trợ script. Cũng được phép là con của phần tử <colgroup>
mà không có thuộc tính
span.
|
| Vai trò ARIA ngầm định | Không có vai trò tương ứng |
| Vai trò ARIA được phép | Không có role nào được phép |
| Giao diện DOM | HTMLTemplateElement |
Đặc tả
| Specification |
|---|
| HTML> # the-template-element> |
Tương thích trình duyệt
Xem thêm
- Thuộc tính HTML
partvàexportparts - Phần tử HTML
<slot> - Lớp giả CSS
:has-slotted,:host,:host(), và:host-context() - Phần tử giả CSS
::partvà::slotted - Giao diện
ShadowRoot - Using templates and slots
- Mô-đun CSS scoping
- Declarative Shadow DOM (with html) trong Using Shadow DOM
- Declarative shadow DOM trên web.dev (2023)