如何正确迭代 getElementsByClassName

IT技术 javascript html dom
2021-02-09 10:01:32

我是 Javascript 初学者。

我正在通过 初始化网页window.onload,我必须通过它们的类名 ( slide)找到一堆元素,并根据某些逻辑将它们重新分配到不同的节点。我有一个函数Distribute(element),它将一个元素作为输入并进行分布。我想做这样的事情(如此此处所述):

var slides = getElementsByClassName("slide");
for(var i = 0; i < slides.length; i++)
{
   Distribute(slides[i]);
}

然而,这对我来说并没有什么神奇之处,因为getElementsByClassName实际上并没有返回数组,而是 a NodeList,它是......

……这是我的猜测……

...在函数内部被改变Distribute(DOM 树在这个函数内部被改变,并且发生了某些节点的克隆)。For-each循环结构也无济于事。

变量 slides 的行为确实不确定,通过每次迭代它都会疯狂地改变它的长度和元素的顺序。

在我的情况下,迭代 NodeList 的正确方法是什么?我正在考虑填充一些临时数组,但不知道该怎么做...

编辑:

我忘记提及的一个重要事实是,另一张幻灯片中可能有一张幻灯片,这实际上是改变slides变量的原因,因为我刚刚发现感谢用户Alohci

对我来说,解决方案是先将每个元素克隆到一个数组中,然后将数组逐个传入Distribute()

6个回答

根据 MDN,从 a 检索项目的方法NodeList是:

nodeItem = nodeList.item(index)

因此:

var slides = document.getElementsByClassName("slide");
for (var i = 0; i < slides.length; i++) {
   Distribute(slides.item(i));
}

我自己还没有尝试过(正常for循环一直对我有用),但试一试。

这是正确的解决方案,除非您尝试查找和更改具有相同类且彼此内部的元素。我在编辑我的问题时解释了我的解决方法。
2021-03-21 10:01:32
如果我可以问,为什么会这样?为什么不实现它以便您能够像这样迭代节点for(var el in document.getElementsByClassName("foo")){}
2021-03-28 10:01:32
for ... of允许您现在像在for (slide of slides) Distribute(slide). 浏览器支持是不完整的,但如果您正在转译,for ... of则将被转换,但NodeList.forEach不会。
2021-03-31 10:01:32
当然,没有考虑到这一点。
2021-04-03 10:01:32

如果您使用新的 querySelectorAll,您可以直接调用 forEach。

document.querySelectorAll('.edit').forEach(function(button) {
    // Now do something with my button
});

根据下面的评论。nodeLists 没有 forEach 函数。

如果将它与 babel 一起使用,您可以添加Array.from它,它将非节点列表转换为 forEach 数组。Array.from不适用于以下浏览器(包括 IE 11)。

Array.from(document.querySelectorAll('.edit')).forEach(function(button) {
    // Now do something with my button
});

在我们昨晚的聚会上,我发现了另一种处理没有 forEach 的节点列表的方法

[...document.querySelectorAll('.edit')].forEach(function(button) {
    // Now do something with my button
});

浏览器支持 [...]

显示为节点列表

显示为节点列表

显示为数组

显示为数组

@wp-overwatch.com 类名中不需要点。正确的版本应该是:[...document.getElementsByClassName('edit')].forEach(function(button) {
2021-03-17 10:01:32
如果我将您的回答与来自 @joshcanhelp 的评论结合起来,那将是一个优雅的解决方案。谢谢 :) 当然,这只会导致多循环的线路优势。
2021-03-24 10:01:32
您应该避免这种情况,因为它可能不适用于所有浏览器。这是我使用的一个简单的解决方法,似乎在任何地方都能完美运行:css-tricks.com/snippets/javascript/...
2021-03-28 10:01:32
问题在于 nodeList 在每个浏览器中都没有 forEach 函数。如果你愿意修改原型,这样做很简单:if ( !NodeList.prototype.forEach ) {NodeList.prototype.forEach = Array.prototype.forEach;}
2021-03-30 10:01:32
我想你的意思是 [...document.getElementsByClassName('.edit')].forEach(function(button) {
2021-04-03 10:01:32

2021 年的最新答案

当提出这个问题时(2013 年),.getElementsBy*方法返回一个NodeList然而,在 2021 年情况并非如此,所有这些 DOM 遍历方法都返回一个实时的HTMLCollection,这getElementsByName是一个例外。

这两个列表之间存在显着差异。HTMLCollection 有两个方法,而 NodeList 有五个方法,包括NodeList.forEach,可用于遍历 NodeList。

实时收藏是有问题的,因为没有办法在幕后保持更新。为了实现可靠的集合,HTMLCollection 的每个当前实现中,每次访问集合时都会遍历 DOM实际上,这意味着,每次访问实时集合的成员(包括长度)时,浏览器都会遍历整个文档以查找特定元素。

标准说:

如果集合是活动的,则该对象上的属性和方法必须对实际的底层数据进行操作,而不是对数据的快照进行操作。

永远不要迭代实时 HTMLCollection!

相反,将集合转换为数组,然后迭代该数组。或者更确切地说,使用 获取元素.querySelectorAll,它为您提供静态 NodeList 和更灵活的选择元素的方式。

如果您确实需要一个活动的元素列表,请使用最接近的可能的共同祖先元素作为上下文而不是document.

值得注意的是,还存在实时 NodeList。实时 NodeList 的示例是Node.childNodesgetElementsByName的返回值

@Nomentsoa 是的,确切地说,或任何循环。
2021-03-21 10:01:32
所以你说的是不要在 getElementsBy* 上使用 for 循环?
2021-04-10 10:01:32

你总是可以使用数组方法:

var slides = getElementsByClassName("slide");
Array.prototype.forEach.call(slides, function(slide, index) {
    Distribute(slides.item(index));
});
@lesolorzanov 'Distribute' 是在其源代码中创建的帖子的所有者的自定义函数。请也阅读问题。
2021-03-12 10:01:32
非常漂亮和美丽的答案,非常感谢!
2021-04-10 10:01:32

我遵循了Alohci的反向循环的建议,因为它是一个实时的nodeList. 这是我为那些好奇的人所做的......

  var activeObjects = documents.getElementsByClassName('active'); // a live nodeList

  //Use a reverse-loop because the array is an active NodeList
  while(activeObjects.length > 0) {
    var lastElem = activePaths[activePaths.length-1]; //select the last element

    //Remove the 'active' class from the element.  
    //This will automatically update the nodeList's length too.
    var className = lastElem.getAttribute('class').replace('active','');
    lastElem.setAttribute('class', className);
  }