JavaScript函数点击后仅修改3个元素中的2个,求失效原因
我写了一个简单的JS函数,点击指定元素时移除3个带hidden类元素的该类,但点击后只有item1和item3生效,item2得再次点击才行。我已经用其他方法解决了,但想知道这段代码失效的原因。
代码如下:
function lmao() { const collection = document.getElementsByClassName("hidden"); console.log(collection.length); for (var i = 0; i < collection.length; i++) { collection[i].classList.remove("hidden"); } }
body { text-align: center; font-size: 27px; } .hidden { display: none; } .nothidden { display: block; }
<div> <span onclick="lmao()">arrow</span> <br><br><br> <span class="hidden">item1</span> <br> <span class="hidden">item2</span> <br> <span class="hidden">item3</span> <br> </div>
原因分析
问题出在getElementsByClassName的返回值上:它返回的是实时的HTMLCollection对象。这个集合会随着DOM的变化自动更新——也就是说,当你移除某个元素的hidden类时,这个元素会立刻从collection中消失,集合的长度和元素索引都会动态改变。
具体执行流程:
- 初始时
collection包含3个元素:[item1, item2, item3],长度为3。 - 第一次循环(
i=0):移除item1的hidden类,item1从集合中被移除,此时collection变成[item2, item3],长度变为2。 i自增到1,此时判断1 < 2成立,取collection[1](也就是原来的item3),移除它的hidden类,item3从集合中消失,collection变成[item2],长度变为1。i自增到2,此时2 < 1不成立,循环直接结束,item2根本没被处理到。
这就是为什么item2需要第二次点击才会生效——第二次点击时,集合里只剩item2,循环会正常处理它。
几种修复方案
1. 将实时集合转为静态数组
把HTMLCollection转换成普通数组,这样后续DOM变化不会影响数组内容:
function lmao() { const collection = Array.from(document.getElementsByClassName("hidden")); // 或者用扩展语法:const collection = [...document.getElementsByClassName("hidden")]; for (var i = 0; i < collection.length; i++) { collection[i].classList.remove("hidden"); } }
2. 使用非实时的NodeList(推荐)
用querySelectorAll代替getElementsByClassName,它返回的是静态的NodeList,不会随DOM变化自动更新:
function lmao() { const collection = document.querySelectorAll(".hidden"); for (var i = 0; i < collection.length; i++) { collection[i].classList.remove("hidden"); } }
3. 倒序循环
从集合的最后一个元素开始处理,即使前面的元素被移除,也不会影响后续元素的索引:
function lmao() { const collection = document.getElementsByClassName("hidden"); for (var i = collection.length - 1; i >= 0; i--) { collection[i].classList.remove("hidden"); } }
内容的提问来源于stack exchange,提问作者Sherlock907
相关产品推荐
相关产品推荐

