template标签不渲染,需JS克隆template.content并用cloneNode(true)获取副本;填纯文本用textContent防XSS;内联事件无效须手动绑定;表单数组name用“[]”而非索引。

template 标签本身不渲染、不执行脚本、不触发资源加载,它只是个“图纸”——真正动态填充内容,必须靠 JS 克隆 + 遍历修改,不能靠字符串替换或 innerHTML 直接注入。
克隆 template.content 后必须用 cloneNode(true),不能直接 appendChild(template.content)
因为 template.content 是一个 DocumentFragment,它只能被插入一次;第二次 append 会移动节点而非复制,结果是“只显示最后一个”,其余全消失。
- ✅ 正确:
const clone = template.content.cloneNode(true)—— 每次都得到干净副本 - ❌ 错误:
container.appendChild(template.content)—— 第二次调用时返回空 fragment - ⚠️ 注意:
cloneNode(false)只克隆顶层节点,不带子元素,填不了内容
填充文本内容优先用 textContent,别用 innerHTML 填纯文本
如果只是塞标题、描述这类无格式文本,textContent 更安全、更快,且天然防 XSS;用 innerHTML 不仅多余,还可能把用户输入里的 、<code>" 解析成标签或属性,破坏结构。
- ✅ 填标题:
clone.querySelector('.product-name').textContent = data.name - ✅ 填说明:
clone.querySelector('p').textContent = data.desc - ❌ 危险:
el.innerHTML = data.desc(data.desc 含<script></script>就执行) - ⚠️ 若真需渲染 HTML 片段(如富文本摘要),先用 DOMPurify 过滤,再设
innerHTML
模板里写 onclick="xxx()" 不会生效,事件必须手动绑定
template 中的内联事件(onclick、oninput 等)在克隆后不会自动注册监听器——浏览器只在解析 HTML 字符串时处理这些属性,而 cloneNode 复制的是节点对象,不是重新解析过程。
立即学习“前端免费学习笔记(深入)”;
- ✅ 正确做法:克隆后立即遍历,对按钮加监听:
clone.querySelector('.add-to-cart').addEventListener('click', handler) - ✅ 推荐用 data-* 属性传参:
<button data-id="123"></button>,避免拼字符串到 onclick - ❌ 模板里写
onclick="addToCart(123)"—— 克隆后点击无响应 - ⚠️ 如果用事件委托(如监听 container 的 click),确保 selector 能匹配克隆后的节点
name 属性含 [] 才能保证表单提交为数组,别手动生成索引
动态生成多个 <input name="items[]">,浏览器会按 DOM 顺序自动聚合成数组;一旦写成 name="items[0]",删除中间项会导致后端收到稀疏索引或重复键名,PHP/Express 解析出错。
- ✅ 统一用:
<input name="tags[]">、<select name="roles[]"></select> - ✅ 删除整行:
item.remove()或item.parentNode.removeChild(item) - ❌ 避免:
name="items[{{i}}]"、name="meta.key"(点号不被浏览器识别) - ⚠️ 若需 JS 侧维护唯一 ID(比如校验状态),用
data-id="abc123",和 name 解耦
最容易被忽略的是:克隆后的节点没有父元素,querySelector 查找范围仅限该 fragment,别误以为它能跨到 document 里找全局样式或已挂载组件;所有查找、修改、绑定,都得在 clone 上做,且必须等它 append 到 document 后,事件才真正可触发。



















