学习前提

理解HTML结构、数组循环、querySelector与textContent。

本课完成后你能做什么

把重复卡片结构与数据分离,避免动态渲染时产生重复id或HTML注入。

先理解机制

template里的内容不会像普通节点一样立即显示,可通过content获得DocumentFragment,再克隆用于渲染。模板描述结构,数组提供内容,渲染函数连接二者。这比拼接长HTML字符串更容易审查字段写入位置。

cloneNode(true)复制子树,但不会复制通过addEventListener注册的监听器。动态项目需要重新绑定,或使用父容器事件委托。给每项记录稳定id,放在data-id中供交互读取;dataset中的值是字符串,比较前统一类型。

模板中的固定id会随克隆重复,破坏label、锚点与查询关系。能用类名定位内部元素就用类名;必须关联label和input时,根据记录id生成唯一标识。data属性只承载少量界面元信息,不是隐私保险箱。

先创建片段再一次插入可以减少零散DOM写入,但不要声称一定显著加速。性能取决于数据量与布局读取方式。示例每次渲染替换列表,适合小列表;复杂表单中需要考虑焦点和未提交状态。

可运行示例

运行方式:将HTML示例保存为独立的.html文件并在浏览器打开;含脚本的示例使用现代浏览器。资源文件与多文件模块另按正文要求准备。

<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Honeysuck practice</title></head><body>
<ul id="courses"></ul><template id="course-template"><li><h2 class="title"></h2><p class="summary"></p><button type="button">Mark studied</button></li></template>
<p id="status" role="status"></p><script>
const data=[{id:1,title:'HTML templates',summary:'Reuse structure safely.'},{id:2,title:'CSS layout',summary:'Arrange content.'}];
const template=document.querySelector('#course-template'),list=document.querySelector('#courses'),fragment=document.createDocumentFragment();
for(const item of data){const clone=template.content.cloneNode(true);clone.querySelector('li').dataset.id=String(item.id);clone.querySelector('.title').textContent=item.title;clone.querySelector('.summary').textContent=item.summary;fragment.append(clone);}list.replaceChildren(fragment);
list.addEventListener('click',event=>{const button=event.target.closest('button');if(!button||!list.contains(button))return;document.querySelector('#status').textContent='Studied course '+button.closest('li').dataset.id;});
</script>
</body></html>

推演与验证

初始应有两张记录。点击第二项按钮得到id2;点击标题没有动作。把标题改成<img>文本,应原样显示。检查DOM确保只有一个course-template,克隆内容没有重复固定id。

常见错误与边界

不要把DOM位置当成课程id,排序后位置会变。若后续加链接,应校验目标协议,textContent只保护文本位置,不会替你验证href。

动手练习

给每张卡片增加复选框与label,点击label应勾选对应项。按标题倒序渲染后身份关系不能变化。

参考解法与理由

用course-id生成input.id,再设置label.htmlFor。每次从数据稳定id生成,而不是用当前数组索引。可用document.querySelectorAll收集id并通过Set检查唯一性。

进一步阅读