做报销单页面,需要一个「添加一行」的按钮,把表格里第一行整行复制到末尾。用 cloneNode 写完后,新行能显示,输入框也能打字,但行尾那个删除按钮怎么点都没反应。

原因很直接:cloneNode 复制的是DOM结构,不复制用 addEventListener 绑上去的事件,也不复制元素上通过JS赋的属性和状态。老代码用 onclick 写在HTML里,克隆后还能用,这也是很多旧例子看起来没问题的地方。

深拷贝和浅拷贝差在哪

cloneNode(true) 连子节点一起复制,cloneNode(false) 只复制外壳。复制一行表格必须用 true,否则拿到一个空的 tr。但深拷贝也有边界:canvas 的画布内容、input 当前输入的值、select 选中的项,都不会跟着过来,值存在 value 属性里的除外。

复制完先处理 id

原行里如果有 id,克隆后页面上就有两个同名 id,getElementById 只会拿到第一个,样式和脚本都可能串。复制后遍历一次,把 id 去掉或者加后缀。

const row = document.querySelector('#tbody tr'); const copy = row.cloneNode(true); copy.removeAttribute('id'); copy.querySelectorAll('[id]').forEach(el => el.removeAttribute('id')); document.querySelector('#tbody').appendChild(copy);

事件用委托,不要重新绑

给 tbody 挂一个 click 监听,用 event.target.closest('button') 判断点的是谁。这样不管克隆多少行都不用重新绑事件,删掉行也不会留下野指针。这套写法在列表增删场景里几乎是标配。

记住一条:cloneNode 只搬结构,行为和状态得自己补。