JS数组存储DOM元素设置样式仅最后一个生效的原因咨询
现象产生的核心原因
出现这个问题的根源是对DOM元素的innerHTML属性执行追加赋值(+=)时,浏览器不会在原有DOM结构上直接新增节点,而是会先销毁容器内所有已存在的DOM节点,再完整解析新的HTML字符串,生成全量的全新DOM节点挂载到页面上。你之前存入数组的DOM元素引用,大部分都是已经被销毁、不在当前渲染DOM树里的游离节点,对这些失效节点做属性修改当然不会反映到页面上。
具体执行逻辑拆解
循环过程中每一步的实际表现是:
- 当
i=0时,第一次给#h的innerHTML追加内容,生成第一个id=0的div,此时把这个div的引用存入数组,这个引用对应页面上的真实节点,暂时有效。 - 当
i=1时,执行innerHTML +=操作,浏览器会直接删掉#h内所有已存在的节点(包括刚才生成的id=0的div),重新解析HTML生成新的id=0、id=1两个div。这时候数组里存的那个id=0的div引用,已经是被销毁的旧节点,和页面上新生成的id=0的div完全是两个独立对象,修改它的样式不会影响页面。 - 后续
i=2、i=3的循环逻辑完全一致:每新增一个div,之前存入数组的所有DOM引用都会全部失效,只有最后一次循环(i=4)时push进数组的div引用,是最后一次DOM重绘后刚获取的真实节点,和页面上渲染的最后一个div是同一个对象,所以修改它的样式才会生效,表现为“只有最后一个div变绿”。
为什么直接用getElementById查询可以正常运行
每次调用
document.getElementById时,都是从当前页面实时渲染的DOM树里查找匹配的最新节点,拿到的永远是当前生效的真实DOM引用,不存在引用失效的问题,所以可以正常依次修改所有div的样式。
正确的数组存储DOM写法
如果要把DOM元素存在数组里批量操作,不要在循环里反复用innerHTML +=触发DOM重绘,可以等所有DOM结构生成完毕后再统一获取引用存储,参考代码如下:
const divArr = []; const container = document.getElementById("h"); // 先一次性拼接好所有HTML内容,只触发一次DOM解析重绘 let htmlContent = "alsdjf;lkajsdf"; for (let i = 0; i < 5; i++) { htmlContent += `<div id=${i}>hello</div>`; } container.innerHTML = htmlContent; // DOM全部生成后,再统一获取节点存入数组 for (let i = 0; i < 5; i++) { divArr.push(document.getElementById(`${i}`)); } let divArrIndex = 0; setInterval(() => { // 这时候数组里的所有引用都是真实有效的,可以直接操作 divArr[divArrIndex].style.backgroundColor = 'green'; divArrIndex = divArrIndex >= 4 ? 0 : divArrIndex + 1; }, 1000);
内容的提问来源于stack exchange,提问作者Sai Nallani
相关产品推荐
相关产品推荐

