getElementsByClassName vs querySelectorAll

getElementsByClassName vs querySelectorAll

本文关键字:querySelectorAll vs getElementsByClassName      更新时间:2023-09-26

如果我使用

var temp = document.querySelectorAll(".class");
for (var i=0, max=temp.length; i<max; i++) { 
 temp[i].className = "new_class";
}

一切都很好。所有节点都会更改其类。但是,使用gEBCN:

var temp = document.getElementsByClassName("class");
for (var i=0, max=temp.length; i<max; i++) { 
 temp[i].className = "new_class";
}  

我弄错了。代码在某个时刻跳出循环,没有完成带有msg"can't set className of null"的作业
我知道这是静态与活动节点列表的问题(我认为),但由于gEBCN要快得多,而且我需要遍历庞大的节点列表(树),所以我真的很想使用getElementsByClassName
我能做些什么来坚持gEBCN而不被迫使用querySelectorAll吗?

这是因为getElementsByClassName返回的HTMLCollection是活动的。

这意味着,如果将"class"添加到某个元素的classList中,它将神奇地出现在temp中。

oposite也是正确的:如果删除temp中元素的"class"类,它将不再存在。

因此,更改类会重新索引集合并更改其长度。所以问题是,你要迭代它,预先获取它的长度,而不考虑索引的变化。

为了避免这个问题,你可以:

  • 使用非实时集合。例如,

    var temp = document.querySelectorAll(".class");
    
  • 将活动HTMLCollection转换为数组。例如,使用其中一个

    temp = [].slice.call(temp);
    temp = Array.from(temp); // EcmaScript 6
    
  • 向后迭代。例如,请参见@Quentin的回答。

  • 考虑到指数的变化。例如,

    for (var i=0; i<temp.length; ++i) { 
     temp[i].className = "new_class";
     --i; // Subtract 1 each time you remove an element from the collection
    }
    
    while(temp.length) { 
     temp[0].className = "new_class";
    }
    

在列表上循环向后,然后元素将从末尾消失(不再查看的地方)。

for (var i = temp.length - 1; i >= 0; i--) { 
  temp[i].className = "new_class";
}  

但是,请注意,IE 8支持querySelectorAll,但不支持getElementsByClassName,因此您可能更喜欢querySelectorAll以获得更好的浏览器支持。


或者,不要删除现有类:

for (var i=0, max=temp.length; i<max; i++) {  
  temp[i].className += " new_class";
}