pin_drop当前位置:知识文库 ❯ 图文

布局结构:HTML模板template - 完整教程与代码示例


一、教程简介

HTML <template> 标签是Web Components技术栈的重要组成部分,它提供了一种声明式的方式来定义可复用的内容模板。template中的内容在页面加载时不会被渲染,只有在JavaScript中通过代码实例化后才会显示。本教程将详细讲解template标签的使用方法、内容模板的创建以及克隆使用技巧。


二、核心概念

什么是template标签

<template> 是HTML5引入的一个特殊元素,用于声明不会被立即渲染的HTML内容片段。它相当于一个"蓝图"或"模具",可以被JavaScript多次克隆和插入到文档中。

template的特点

特点 说明
不渲染 template中的内容不会在页面加载时显示
不活跃 template中的脚本不会执行,图片不会加载,样式不会应用
无效DOM template内容不在活动DOM树中,querySelector无法搜索到
可克隆 可以通过JavaScript克隆template内容并插入DOM

template vs 普通隐藏元素

代码示例

<!-- 普通隐藏:内容仍在DOM中,脚本会执行,样式会应用 -->
<div style="display:none;">
    <img src="image.jpg" alt="图片">  <!-- 会加载 -->
    <script>alert('执行了');</script>  <!-- 会执行 -->
</div>

<!-- template:内容不在活动DOM中,不加载不执行 -->
<template id="myTemplate">
    <img src="image.jpg" alt="图片">  <!-- 不会加载 -->
    <script>alert('不会执行');</script>  <!-- 不会执行 -->
</template>

三、语法与用法

基本语法

代码示例

<template id="templateId">
    <!-- 模板内容 -->
    <div class="card">
        <h3>标题</h3>
        <p>内容</p>
    </div>
</template>

JavaScript操作template

代码示例

// 1. 获取template元素
const template = document.getElementById('myTemplate');

// 2. 获取template的内容(DocumentFragment)
const content = template.content;

// 3. 克隆template内容
const clone = template.content.cloneNode(true);  // 深克隆

// 4. 修改克隆内容
clone.querySelector('h3').textContent = '新标题';

// 5. 插入到DOM
document.getElementById('container').appendChild(clone);

template的关键属性

属性/方法 说明
content 返回template内容的DocumentFragment
cloneNode(true) 深克隆template内容
innerHTML 读写template的HTML内容

四、代码示例

示例1:template基本使用

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>template基本使用</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: "Microsoft YaHei", sans-serif;
            background: #f5f5f5;
            padding: 20px;
        }
        h1 { color: #2c3e50; margin-bottom: 20px; }
        .controls { margin-bottom: 20px; }
        .btn {
            padding: 10px 20px;
            background: #3498db;
            color: white;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-size: 14px;
            margin-right: 10px;
        }
        .btn:hover { background: #2980b9; }
        .btn-danger { background: #e74c3c; }
        .btn-danger:hover { background: #c0392b; }
        .card-container {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
            gap: 20px;
        }
        .user-card {
            background: white;
            border-radius: 8px;
            padding: 20px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
            transition: transform 0.3s;
        }
        .user-card:hover { transform: translateY(-3px); }
        .user-card .avatar {
            width: 60px; height: 60px;
            border-radius: 50%;
            background: #3498db;
            color: white;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 24px;
            font-weight: bold;
            margin-bottom: 15px;
        }
        .user-card h3 { color: #2c3e50; margin-bottom: 5px; }
        .user-card .role { color: #3498db; font-size: 13px; margin-bottom: 10px; }
        .user-card p { color: #666; font-size: 14px; line-height: 1.6; }
    </style>
</head>
<body>
    <h1>template模板使用演示</h1>
    <div class="controls">
        <button class="btn" onclick="addCard()">添加卡片</button>
        <button class="btn btn-danger" onclick="clearCards()">清空所有</button>
    </div>
    <div class="card-container" id="cardContainer"></div>
    <!-- 用户卡片模板 -->
    <template id="userCardTemplate">
        <div class="user-card">
            <div class="avatar"></div>
            <h3 class="name"></h3>
            <div class="role"></div>
            <p class="bio"></p>
        </div>
    </template>
    <script>
        var users = [
            { name: '张三', role: '前端工程师', bio: '专注于React和Vue开发', initial: '张' },
            { name: '李四', role: '后端工程师', bio: '精通Java和Go语言', initial: '李' },
            { name: '王五', role: 'UI设计师', bio: '擅长用户体验设计', initial: '王' }
        ];
        var currentIndex = 0;
        function addCard() {
            if (currentIndex >= users.length) { alert('所有卡片已添加完毕!'); return; }
            var template = document.getElementById('userCardTemplate');
            var clone = template.content.cloneNode(true);
            var user = users[currentIndex];
            clone.querySelector('.avatar').textContent = user.initial;
            clone.querySelector('.name').textContent = user.name;
            clone.querySelector('.role').textContent = user.role;
            clone.querySelector('.bio').textContent = user.bio;
            document.getElementById('cardContainer').appendChild(clone);
            currentIndex++;
        }
        addCard(); addCard(); addCard();
    </script>
</body>
</html>

示例2:template高级应用 - 动态表格

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>template高级应用</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { font-family: "Microsoft YaHei", sans-serif; background: #f5f5f5; padding: 20px; }
        h1 { color: #2c3e50; margin-bottom: 20px; }
        .panel { background: white; border-radius: 8px; padding: 20px; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
        .btn { padding: 8px 20px; background: #3498db; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; }
        .btn:hover { background: #2980b9; }
        .btn-sm { padding: 4px 12px; font-size: 12px; }
        .btn-danger { background: #e74c3c; }
        .btn-danger:hover { background: #c0392b; }
        table { width: 100%; border-collapse: collapse; }
        th, td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #eee; font-size: 14px; }
        th { background: #f8f9fa; color: #2c3e50; font-weight: bold; }
        .status { display: inline-block; padding: 2px 10px; border-radius: 12px; font-size: 12px; font-weight: bold; }
        .status-active { background: #d4edda; color: #155724; }
        .status-inactive { background: #f8d7da; color: #721c24; }
        .status-pending { background: #fff3cd; color: #856404; }
    </style>
</head>
<body>
    <h1>template动态表格</h1>
    <div class="panel">
        <table>
            <thead><tr><th>ID</th><th>姓名</th><th>部门</th><th>状态</th><th>操作</th></tr></thead>
            <tbody id="tableBody"></tbody>
        </table>
    </div>
    <!-- 行模板 -->
    <template id="rowTemplate">
        <tr>
            <td class="cell-id"></td>
            <td class="cell-name"></td>
            <td class="cell-dept"></td>
            <td class="cell-status"></td>
            <td><button class="btn btn-sm btn-danger" onclick="deleteRow(this)">删除</button></td>
        </tr>
    </template>
    <script>
        var idCounter = 1;
        var statusMap = {
            active: { text: '在职', class: 'status-active' },
            inactive: { text: '离职', class: 'status-inactive' },
            pending: { text: '试用期', class: 'status-pending' }
        };
        function addRecord(name, dept, status) {
            var template = document.getElementById('rowTemplate');
            var clone = template.content.cloneNode(true);
            clone.querySelector('.cell-id').textContent = idCounter++;
            clone.querySelector('.cell-name').textContent = name;
            clone.querySelector('.cell-dept').textContent = dept;
            var statusInfo = statusMap[status];
            var statusSpan = document.createElement('span');
            statusSpan.className = 'status ' + statusInfo.class;
            statusSpan.textContent = statusInfo.text;
            clone.querySelector('.cell-status').appendChild(statusSpan);
            document.getElementById('tableBody').appendChild(clone);
        }
        addRecord('张三', '技术部', 'active');
        addRecord('李四', '产品部', 'pending');
        addRecord('王五', '设计部', 'active');
    </script>
</body>
</html>

五、浏览器兼容性

特性 Chrome Firefox Safari Edge IE
template基本支持 26+ 22+ 7.1+ 13+ 不支持
template.content 26+ 22+ 7.1+ 13+ 不支持
cloneNode 26+ 22+ 7.1+ 13+ 不支持

IE兼容性处理

代码示例

// 检测template支持
if ('content' in document.createElement('template')) {
    // 原生支持
} else {
    // IE回退方案:使用script标签
    // <script type="text/template" id="myTemplate">...</script>
    var html = document.getElementById('myTemplate').innerHTML;
    var div = document.createElement('div');
    div.innerHTML = html;
    document.getElementById('container').appendChild(div.firstElementChild);
}

六、注意事项与最佳实践

1. 始终使用深克隆

代码示例

// 推荐:深克隆(true参数)
var clone = template.content.cloneNode(true);

// 不推荐:浅克隆(只克隆DocumentFragment本身,不包含子节点)
var clone = template.content.cloneNode(false);

2. template内容是DocumentFragment

代码示例

// DocumentFragment的特点:插入DOM后自动解包
var fragment = template.content.cloneNode(true);
// 修改fragment中的元素
fragment.querySelector('.title').textContent = '新标题';
// 插入时,fragment本身不会成为DOM节点,其子节点直接插入
container.appendChild(fragment);

3. 避免在template中使用id

代码示例

<!-- 不推荐:多次克隆会产生重复id -->
<template id="cardTemplate">
    <div id="card">...</div>
</template>

<!-- 推荐:使用class -->
<template id="cardTemplate">
    <div class="card">...</div>
</template>

4. 模板内容中的事件绑定

代码示例

// 方式1:克隆后绑定事件
var clone = template.content.cloneNode(true);
clone.querySelector('.btn').addEventListener('click', handleClick);
container.appendChild(clone);

// 方式2:使用事件委托(推荐)
container.addEventListener('click', function(e) {
    if (e.target.classList.contains('btn')) {
        handleClick(e);
    }
});

七、代码规范示例

代码示例

<!-- 模板定义规范 -->
<template id="cardTemplate">
    <div class="card" data-type="user">
        <div class="card__header">
            <h3 class="card__title"></h3>
        </div>
        <div class="card__body">
            <p class="card__text"></p>
        </div>
    </div>
</template>

<script>
    // 模板使用规范
    function createCard(data) {
        // 1. 获取模板
        var template = document.getElementById('cardTemplate');

        // 2. 深克隆内容
        var clone = template.content.cloneNode(true);

        // 3. 填充数据
        clone.querySelector('.card__title').textContent = data.title;
        clone.querySelector('.card__text').textContent = data.text;

        // 4. 返回克隆节点(由调用者决定插入位置)
        return clone;
    }

    // 使用
    var card = createCard({ title: '标题', text: '内容' });
    document.getElementById('container').appendChild(card);
</script>

八、常见问题与解决方案

常见问题

克隆后事件监听器丢失怎么办?

原因:cloneNode不会克隆事件监听器。
解决方案:克隆后重新绑定事件,或使用事件委托方式管理动态元素的事件。

template中的样式不生效如何处理?

原因:template内容不在活动DOM中,其中的<style>在克隆插入后才生效。
解决方案:将样式放在template外部或主文档的<style>中。

多次克隆产生重复id怎么解决?

解决方案:使用class替代id,或在克隆后动态生成唯一id。推荐在模板中统一使用class来标识元素。


九、总结

HTML template是创建可复用内容模板的重要工具:

  • 惰性渲染:template内容不会自动渲染,节省资源

  • 安全隔离:template中的脚本不执行,图片不加载

  • 高效克隆:通过cloneNode快速创建多个实例

  • DocumentFragment:template.content是DocumentFragment,插入时自动解包

  • 与Web Components配合:template是Shadow DOM和Custom Elements的基础

  • 避免重复id:多次克隆template时使用class而非id

  • 事件委托:对动态克隆的元素使用事件委托管理事件

标签: template标签 Web Components 内容模板 克隆节点 HTML5 事件委托

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: 布局结构:HTML Grid布局 - 完整教程与代码示例 下一篇: 布局结构:HTML slot插槽 - 完整教程与代码示例

poll相关推荐