从 getElementsByClassName 迭代 HTMLCollection 时的奇怪行为

IT技术 javascript html getelementsbyclassname htmlcollection
2021-01-31 10:09:57

我写了一个函数来改变元素的类来改变它们的属性。出于某种原因,只有一些元素发生了变化。我花了几个小时才找到解决方案,但对我来说似乎很奇怪。也许你可以向我解释一下。

这不起作用:

function replace(){
  var elements = document.getElementsByClassName('classOne');

  for (var i = 0; i < elements.length; i++) {
    elements[i].className = 'classTwo';               
  }
}

请参阅 JSFiddle:仅每隔一个项目受到影响;只有每隔一个红色元素就会将颜色变为蓝色。

所以我将for循环的最终表达式更改为不再递增i

function replace(){
  var elements = document.getElementsByClassName('classOne');

  for (var i = 0; i < elements.length; i) { // Here’s the difference
    elements[i].className = 'classTwo';               
  }
}

这很好用!似乎push被调用并且不需要增量。这是正常的吗?这与我见过的例子不同。

3个回答

正在发生的是一个奇怪的副作用。当您className为 的每个元素重新分配elements,该元素将从数组中删除!(实际上,正如@ user2428118 指出的那样,elements是一个类数组对象,而不是数组。请参阅此线程以了解区别。)这是因为它不再具有classOne类名。当您的循环退出时(在第二种情况下),elements数组将为空。

您可以将循环编写为:

while (elements.length) {
    elements[0].className = 'classTwo'; // removes elements[0] from elements!
}

在您的第一种情况下,通过增加i,您将跳过具有 class 的(原始)元素的一半classOne

顺便说一句,很好的问题。仔细研究和清楚。

谢谢!听起来不错……但是……这是有意义的编程吗?“元素”是一个存在对象,而不是可以在没有你的情况下改变的动态对象?
2021-03-16 10:09:57
你可能应该提到元素实际上不是一个数组,而只是一个类似数组的数据结构
2021-03-31 10:09:57
@MeNa - 虽然elements变量对于您的函数来说是局部的,但它引用的数组也可以被 DOM 访问(并由其维护)。className每个元素属性实际上会触发 DOM 机制中的 setter 函数。该函数的副作用之一是从elements引用的数组中删除元素所以它不会改变“没有你”;它会发生变化,因为您更改了className其中一个元素属性。引用古人的话:“这不是错误;这是一个功能!”
2021-04-05 10:09:57
@TedHopp - 再次感谢您!现在一切都清楚了。(PS,感谢'while()',这是一个很好且优雅的解决方案。)
2021-04-13 10:09:57

getElementsByClassName返回一个节点列表。NodeList 集合是一个实时集合,这意味着文档的修改会影响集合。更多的

恩克斯。事实上,“现场收藏”一词很好地解释了这一点。
2021-04-01 10:09:57
并非所有 NodeList 集合都是实时的。一些“获取”方法,例如 querySelectorAll(),返回不受后续 DOM 更改影响的 STATIC NodeList。
2021-04-05 10:09:57

或恢复循环,从长度为 1 开始并逐步降低到 0

是的,这也可以,但问题需要解释而不是解决方案。
2021-04-01 10:09:57