如何正确地通过getElementsByClassName进行迭代


100

我是Javascript初学者。

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

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

但这对我getElementsByClassName没有任何帮助,因为它实际上并不返回数组,而是一个NodeList,即...

这是我的推测

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

变量滑块的行为实际上是不确定的,通过每次迭代,它会疯狂地更改其长度和元素顺序。

在我的情况下,迭代NodeList的正确方法是什么?我当时正在考虑填充一些临时数组,但不确定如何做到这一点...

编辑:

我忘了提到的一个重要事实是,其中可能有一张幻灯片,而实际上正是slides由于用户Alohci而改变了变量,正如我刚刚发现的那样。

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


3
这实际上是这样做的方式,因此您必须弄乱其他东西!
adeneo

Distribute()函数是冗长而复杂的,可以在此处复制,但是我可以确定我正在更改内部的DOM结构,同时也在其中复制(克隆)元素。当我调试它时,我可以看到变量slides每次在内部传递时都会发生变化。
Kupto

除非您实际在某处进行更改,否则它不会更改。
adeneo 2013年

5
我相信这会getElementsByClassName()返回live nodeList,因此,随着该类的元素被添加nodeList,您迭代更改的长度就增加了。
大卫说恢复莫妮卡

2
@ Kupto-反向循环通常可以解决此类问题,在这种情况下,由于David Thomas给出的原因,Distribute函数删除或移动了元素,使其不再与getElementsByClassName调用匹配。
Alohci 2013年

Answers:


129

根据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循环总是对我有用),但请尝试一下。


这是正确的解决方案,除非您尝试查找和更改具有相同类并且彼此之间的元素。我在编辑问题时解释了解决方法。
Kupto

当然,没有考虑到这一点。
Xing Albert

请问我为什么这样呢?为什么不实现它,以便您可以像这样遍历节点for(var el in document.getElementsByClassName("foo")){}
Nearoo

3
for ... of允许您像中一样立即遍历NodeList for (slide of slides) Distribute(slide)。浏览器支持不完整,但是如果您for ... of进行转码,则会进行转换,但NodeList.forEach不会进行转换。
Mr5o1

67

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

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

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

如果与babel一起使用,则可以添加Array.from,它将非节点列表转换为forEach数组。Array.from在低于IE 11(包括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
});

浏览器对[...]的支持

显示为节点列表

显示为节点列表

显示为数组

显示为数组


4
不可思议的是,nodeLists在每个浏览器中都没有forEach函数。如果您愿意尝试修改原型,那么它就很简单:if ( !NodeList.prototype.forEach ) {NodeList.prototype.forEach = Array.prototype.forEach;}
joshcanhelp

如果我将您的答案与@joshcanhelp的评论相结合,则是一个优雅的解决方案。谢谢:)当然,这只会带来多个循环的线路优势。
yarwest

1
您应该避免这种情况,因为它可能不适用于所有浏览器。这是我使用的一种简单的解决方法,似乎在任何地方都可以正常使用:css-tricks.com/snippets/javascript/…–
tixastronauta

我认为您的意思是[...document.getElementsByClassName('.edit')].forEach(function(button) {
wp-overwatch.com

@ wp-overwatch.com在类名中不需要点。正确的版本应为:[...document.getElementsByClassName('edit')].forEach(function(button) {
MXT

11

您可以始终使用数组方法:

var slides = getElementsByClassName("slide");
Array.prototype.forEach.call(slides, function(slide, index) {
    Distribute(slides.item(index));
});

非常漂亮的答案,非常感谢!
Olga Farber

什么是分配?
lesolorzanov

7

我遵循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);
  }

1
 <!--something like this--> 
<html>
<body>



<!-- i've used for loop...this pointer takes current element to apply a 
 particular change on it ...other elements take change by else condition 
-->  


<div class="classname" onclick="myFunction(this);">first</div>  
<div class="classname" onclick="myFunction(this);">second</div>


<script>
function myFunction(p) {
 var x = document.getElementsByClassName("classname");
 var i;
 for (i = 0; i < x.length; i++) {
    if(x[i] == p)
    {
x[i].style.background="blue";
    }
    else{
x[i].style.background="red";
    }
}
}


</script>
<!--this script will only work for a class with onclick event but if u want 
to use all class of same name then u can use querySelectorAll() ...-->




var variable_name=document.querySelectorAll('.classname');
for(var i=0;i<variable_name.length;i++){
variable_name[i].(--your option--);
}



 <!--if u like to divide it on some logic apply it inside this for loop 
 using your nodelist-->

</body>
</html>

0

我在迭代过程中遇到了类似的问题,然后降落在这里。也许其他人也在犯同样的错误。

就我而言,选择器根本不是问题。问题是我弄乱了javascript代码:我有一个循环和一个子循环。子循环也i用作计数器,而不是j,因此,由于子循环覆盖i了主循环的值,因此该子循环永远不会进行第二次迭代。

var dayContainers = document.getElementsByClassName('day-container');
for(var i = 0; i < dayContainers.length; i++) { //loop of length = 2
        var thisDayDiv = dayContainers[i];
        // do whatever

        var inputs = thisDayDiv.getElementsByTagName('input');

        for(var j = 0; j < inputs.length; j++) { //loop of length = 4
            var thisInput = inputs[j];
            // do whatever

        };

    };
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.