HTML5 引入的 <template> 标签是一个强大的工具,它允许开发者声明一段可复用的、在页面加载时不会立即渲染的 HTML 代码片段。它为构建动态内容提供了更优雅、更高效的解决方案。
一、<template> 标签的用途
<template> 标签的核心目的是作为客户端内容的“模板”。其内容具有以下关键特性:
惰性解析:浏览器在解析 HTML 时,会识别 <template> 标签,但不会渲染其内部内容。内部的图像、脚本、样式表等资源也不会被加载或执行。
文档片段:<template> 的
.content属性是一个只读的DocumentFragment节点。这是一个轻量级的文档对象,可以高效地插入到 DOM 中。隔离性:模板内的内容存在于一个独立的文档片段中,使用
document.getElementById()等方法在主文档中无法查询到其内部元素,避免了 ID 冲突等问题。可复用性:同一个模板可以被实例化多次,生成多个独立的副本,非常适合用于渲染列表、重复的 UI 组件等场景。
传统的 JavaScript 字符串拼接或 innerHTML 方式构建复杂 HTML 结构时,代码可读性差且容易出错。而 <template> 标签允许你像编写普通 HTML 一样编写模板,保持了 HTML 的结构清晰性。
二、基本语法与使用步骤
使用 <template> 标签通常遵循以下步骤:
1. 定义模板
在 HTML 文档的任意位置(通常在 <body> 末尾或 <head> 中)声明你的模板。模板内容可以包含任何有效的 HTML 元素、样式和脚本。
<!DOCTYPE html>
<html>
<head>
<title>Template 示例</title>
</head>
<body>
<h1>用户列表</h1>
<div id="userContainer"></div>
<!-- 定义模板 -->
<template id="userCardTemplate">
<div class="user-card">
<img class="avatar" src="https://www.ipipp.com/images/placeholder.jpg" alt="头像">
<h2 class="name"></h2>
<p class="email"></p>
<button class="detail-btn">查看详情</button>
</div>
<style>
.user-card { border: 1px solid #ccc; padding: 15px; margin: 10px; border-radius: 5px; }
.avatar { width: 50px; height: 50px; border-radius: 50%; }
</style>
<script>
// 注意:模板内的脚本只在模板内容被插入文档后执行一次
console.log('模板脚本加载');
</script>
</template>
<script src="app.js"></script>
</body>
</html>2. 获取模板引用
在 JavaScript 中,通过常规的 DOM 选择方法获取 <template> 元素。
// 获取模板元素
const template = document.getElementById('userCardTemplate');3. 克隆模板内容
使用 document.importNode() 或直接访问 template.content 并克隆其节点。为了能够重复使用,通常使用 cloneNode(true) 进行深克隆。
// 克隆模板内容(DocumentFragment) const contentClone = document.importNode(template.content, true); // 或者 // const contentClone = template.content.cloneNode(true);
4. 操作与填充数据
在将克隆的节点插入 DOM 之前,可以像操作普通 DOM 一样,为其内部元素填充数据。
// 在克隆的片段中查询元素并填充数据
contentClone.querySelector('.name').textContent = '张三';
contentClone.querySelector('.email').textContent = 'zhangsan@example.com';
contentClone.querySelector('.avatar').src = `https://www.ipipp.com/avatars/zhangsan.png`;
// 添加事件监听器
contentClone.querySelector('.detail-btn').addEventListener('click', () => {
alert('查看用户:张三');
});5. 插入到文档中
将处理好的文档片段插入到页面中期望的位置。
// 获取容器
const container = document.getElementById('userContainer');
// 将克隆并修改后的内容插入文档
container.appendChild(contentClone);三、完整示例:动态生成列表
下面是一个完整的示例,演示如何使用 <template> 动态生成一个用户列表。
<!DOCTYPE html>
<html>
<head>
<style>
.user-item { padding: 10px; border-bottom: 1px solid #eee; }
.user-item.active { background-color: #e3f2fd; }
</style>
</head>
<body>
<ul id="userList"></ul>
<template id="userItemTemplate">
<li class="user-item">
<span class="username"></span>
<span class="role"></span>
</li>
</template>
<script>
// 模拟数据
const users = [
{ id: 1, name: 'Alice', role: '管理员' },
{ id: 2, name: 'Bob', role: '编辑' },
{ id: 3, name: 'Charlie', role: '订阅者' }
];
const template = document.getElementById('userItemTemplate');
const listContainer = document.getElementById('userList');
users.forEach(user => {
// 克隆模板内容
const clone = document.importNode(template.content, true);
// 填充数据
clone.querySelector('.username').textContent = user.name;
clone.querySelector('.role').textContent = `(${user.role})`;
// 获取列表项元素并添加更多属性或事件
const listItem = clone.querySelector('li');
listItem.dataset.userId = user.id;
listItem.addEventListener('click', function() {
document.querySelectorAll('.user-item').forEach(item => item.classList.remove('active'));
this.classList.add('active');
console.log(`选中用户ID: ${this.dataset.userId}`);
});
// 插入到列表
listContainer.appendChild(clone);
});
</script>
</body>
</html>四、与 <script type=“text/template”> 的对比
在 <template> 标签出现之前,开发者常使用 <script type="text/template"> 来存储模板。两者主要区别如下:
<template>:是 HTML5 标准元素,其内容是真正的 DOM 节点(DocumentFragment),可以直接用 DOM API 操作,更高效、更符合语义。
<script type="text/template">:浏览器将其内容视为纯文本,不解析为 DOM。使用时需要获取其
textContent,然后通过innerHTML或字符串处理库(如 Handlebars, Mustache)来转换为 HTML,性能相对较低。
五、注意事项与最佳实践
浏览器支持:所有现代浏览器都支持 <template> 标签。对于旧版浏览器,<template> 标签内的内容可能会被渲染出来。可以通过 CSS 进行隐藏:
template { display: none; }(现代浏览器会自动处理,但这是一个安全的降级方案)。脚本执行时机:模板内包含的 <script> 代码,只有在模板内容被插入到主文档中时才会执行。每插入一次,脚本就执行一次。
样式作用域:模板内定义的 <style> 样式,在模板内容插入文档后,会作用于整个文档,而不仅仅是模板实例。需要注意样式冲突。
性能:对于需要大量重复创建的复杂结构,使用 <template> 比用 JavaScript 拼接 HTML 字符串性能更好,因为浏览器只需要在首次解析时处理一次模板的 HTML 结构。
结合 Web Components:<template> 是创建自定义元素(Custom Elements)和影子 DOM(Shadow DOM)的理想搭档,用于定义组件的内部结构。
总之,<template> 标签将 HTML 的声明式力量与 JavaScript 的动态能力完美结合。它提供了一种结构清晰、易于维护且性能优异的方式来生成动态内容,是现代 Web 开发中不可或缺的工具。