name 属性必须统一使用 [] 语法,避免手动编号或点号,确保浏览器按 DOM 顺序自动聚合为连续数组;删除需移除整个 DOM 节点,新增应通过 template 克隆,动态表格 key 必须从接口返回获取。

name 属性必须用 [],别手动编号
动态表单提交后数据丢失、后端收不到数组、稀疏索引(如只有 items[0] 和 items[2])——这些问题几乎都源于在 name 里写了具体数字。浏览器原生只认 [] 语义,它会按 DOM 节点顺序自动聚合成连续数组;写 items[0] 就等于把 JS 的索引逻辑硬塞进 HTML,删除中间项后,那个 items[2] 还在 DOM 里,照样提交,但后端解析时可能直接跳过空位。
- ✅ 统一用
name="items[]"、name="files[][name]"、name="entries[][label]" - ❌ 避免
name="items[0]"、name="meta[key1]"、name="user.profile.name"(点号是后端框架语法,浏览器不解析) - ⚠️ 混用结构(比如一部分用
[],一部分用[0])会导致 PHP/Express 解析成混合类型:数组里嵌对象,或键名被转成字符串而非整数索引
删除必须移除整个 DOM 节点,不能只清空 value
只设 input.value = "" 或调用 input.remove() 却没删父容器,会造成表单提交时带空值、FormData 里残留字段、JS 判断“该项还存在”但实际已无效。关键不是删不删得干净,而是“是否还在提交流中”。
- ✅ 删除前确认:
if (confirm("确定删除?")) { item.parentNode.removeChild(item); } - ✅ 容器要包裹完整一项:
<div class="form-item"><input name="tags[]"><button onclick="removeItem(this)">×</button></div> - ✅ 删除函数里取
button.parentNode,再调removeChild()或直接item.remove() - ⚠️ 如果用了
FormData.append()手动收集,DOM 删除后必须同步调用formData.delete('key'),否则仍会提交
新增一律用 template + cloneNode(true),别靠 querySelectorAll.length 算索引
用 document.querySelectorAll('input[name="items[]"]').length 来拼 name="items[2]",本质是把 JS 的状态管理逻辑和浏览器的表单机制耦死。用户手动删了一项,JS 算出来是 2,但 DOM 里已有 items[2] 的旧节点,结果重复提交。
- ✅ 使用
<template id="row-template"><div><input name="items[]"></div></template> - ✅ 新增时:
const clone = document.getElementById('row-template').content.cloneNode(true); container.appendChild(clone); - ✅ 若需 JS 侧维护映射关系(比如关联 ID 或校验状态),用
data-id或data-index,和name解耦 - ⚠️
template内避免写固定id,否则克隆后多个同名 ID,破坏 DOM 唯一性
动态表格填充时,单元格 key 必须从接口返回中取,别手写 column1/column2
e签宝等合同平台的动态表格控件,其单元格 key(如 column1、column2)由系统自动生成并随模板绑定,不是按顺序命名的。手写 key 会导致填充失败或错位,尤其当模板编辑后列顺序变动、新增/删除列时。
立即学习“前端免费学习笔记(深入)”;
- ✅ 调用【查询合同模板中控件详情】接口,从返回 JSON 的
tableContent.row对象里取真实 key - ✅ 示例响应片段:
{"tableContent": {"row": [{"column1": "张三", "column2": "28"}, ...]}}→ 填充时用column1,不是猜 - ⚠️ 上传 .docx 转 HTML 时,
convertToHTML=true是必需参数;直接上传 HTML 文件不支持 - ⚠️ 【获取制作合同模板页面】链接有效期仅 24 小时,过期需用模板 ID 调【获取编辑合同模板页面】重新获取
name,都在定义后端接收的数据契约。



















