为何这段JavaScript代码仅修改最后一个div元素?
问题解析:为何仅最后一个div被更新?
先看你提供的核心代码:
JavaScript代码
let names = ["a", "b", "c"]; let objs = []; let container; let form; class Class { constructor(name) { this.name = name; this.buildHTML(); } buildHTML() { container.innerHTML += ` <div id="${this.name}"> ${this.name} </div>` this.inner_container = document.getElementById(this.name); } updateHTML() { this.inner_container.innerHTML = "test"; // document.getElementById(this.name).innerHTML = "test"; } } function main() { container = document.getElementById("container"); for (var name in names) { objs.push(new Class(names[name])); } form = document.getElementById("form"); form.addEventListener('submit', eventHandler_submit_form); } function eventHandler_submit_form(event) { event.preventDefault(); for (var obj in objs) { objs[obj].updateHTML(); } }
HTML代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Sample Text</title> </head> <body onload="main()"> <form id="form"> <button type="submit">Submit</button> </form> <div id="container"></div> </body> </html>
你发现点击按钮时,只有标记为c的div被改成了test,换成注释里的写法就正常。这背后的核心是DOM元素引用的有效性问题,咱们来详细拆解:
两种写法的本质区别
this.inner_container.innerHTML = "test":操作的是缓存的DOM元素引用——这个引用是在buildHTML阶段保存的,指向当时创建的div节点。document.getElementById(this.name).innerHTML = "test":每次调用都会重新从当前DOM树中查找对应元素,拿到的是页面上真实存在的最新节点。
为什么第一种写法只生效最后一个?
问题出在buildHTML里的container.innerHTML +=这行代码。当你用innerHTML +=时,浏览器的执行逻辑是:
- 把container当前的所有内容转换成HTML字符串
- 拼接上新的HTML片段
- 将拼接后的字符串重新解析成DOM元素,完全替换掉container里原来的所有旧元素
也就是说,每次创建新的Class实例时:
- 第一次创建
a的div,this.inner_container指向这个正常存在的div - 第二次创建
b的div时,innerHTML +=会销毁原来的adiv,重新生成a和b两个新div。此时a实例里的this.inner_container引用的是已经被销毁的旧元素,不再对应页面上的任何节点 - 第三次创建
c的div时,同样销毁a、b的旧元素,重新生成三个新div。只有c实例的this.inner_container指向的是当前页面上存在的cdiv
所以当调用updateHTML时,前两个实例的this.inner_container是无效的(指向已被删除的元素),自然不会有任何效果;只有最后一个实例的引用是有效的,因此只有c的div被更新。
为什么第二种写法能正常工作?
document.getElementById(this.name)每次都会去当前活跃的DOM树里查找对应id的元素,不管之前的元素有没有被销毁重建,它总能找到页面上真实存在的那个div。所以三个实例调用时,都能正确定位到目标元素并更新内容。
额外的修复方案(保留缓存引用)
如果你不想每次都查DOM,想保留元素引用的方式,可以换掉innerHTML +=,改用document.createElement来创建元素并添加:
buildHTML() { const div = document.createElement('div'); div.id = this.name; div.textContent = this.name; container.appendChild(div); this.inner_container = div; }
这种方式不会销毁之前的元素,每个实例的this.inner_container都会指向页面上真实存在的DOM节点,后续更新就能全部生效。
内容的提问来源于stack exchange,提问作者Qwox
相关产品推荐
相关产品推荐

