Как правильно перебирать getElementsByClassName


102

Я новичок в 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структура петли тоже не помогает.

Переменные слайды действуют действительно недетерминированно, на каждой итерации они дико меняют длину и порядок элементов.

Как правильно перебирать NodeList в моем случае? Я думал о заполнении временного массива, но не знаю, как это сделать ...

РЕДАКТИРОВАТЬ:

Важный факт, который я забыл упомянуть, - это то, что один слайд может быть внутри другого, это на самом деле то, что меняет slidesпеременную, как я только что узнал благодаря пользователю Alohci .

Решение для меня заключалось в том, чтобы сначала клонировать каждый элемент в массив, а затем передавать массив по очереди Distribute().


3
Это на самом деле способ сделать это, так что вы, должно быть, напутали что-то еще!
adeneo 05

Distribute()функция является длительным и сложным , чтобы скопировать здесь, но я уверен , что я изменяю структуру DOM внутри, я также тиражирование (клонирование) элементы там. Когда я отлаживаю его, я вижу, что переменная slidesменяется каждый раз, когда она передается внутрь.
Kupto

Он не изменится, пока вы его где-то не измените.
adeneo 05

5
Я считаю, что это getElementsByClassName()возвращает live nodeList, так как элементы с этим классом добавляют длину, nodeListпо которой вы повторяете изменения.
Дэвид просит восстановить Монику

2
@ Kupto- цикл в обратном направлении часто решает проблему такого рода, когда функция Distribute удаляет или перемещает элемент таким образом, что он больше не соответствует вызову getElementsByClassName по причине, которую дает Дэвид Томас.
Alohci 05

Ответы:


131

Согласно 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

Конечно, не учел.
Альберт Син

Могу я спросить, почему это так? Почему он не реализован так, чтобы вы могли перебирать узлы таким образом for(var el in document.getElementsByClassName("foo")){}?
Nearoo

3
for ... ofпозволяет вам перебирать NodeList сейчас, как в for (slide of slides) Distribute(slide). Поддержка браузером неоднородна, но если вы транспилируете, она for ... ofбудет преобразована, но NodeList.forEachне будет.
Mr5o1

69

Если вы используете новый querySelectorAll, вы можете вызвать forEach напрямую.

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

В комментарии ниже. В списках узлов нет функции 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
});

Браузерная поддержка [...]

Отображение в виде списка узлов

Отображение в виде списка узлов

Отображается как массив

Отображается как массив


4
Понятно, что в списках узлов нет функции forEach в каждом браузере. Если вы готовы повозиться с прототипами, это достаточно просто сделать:if ( !NodeList.prototype.forEach ) {NodeList.prototype.forEach = Array.prototype.forEach;}
joshcanhelp

Элегантное решение, если я объединю ваш ответ с комментарием от @joshcanhelp. Спасибо :) Конечно, это приведет только к преимуществу линии с несколькими петлями.
yarwest 03

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));
});

очень красивый и красивый ответ, большое спасибо!
Ольга Фарбер

1
Что такое распространение?
lesolorzanov

7

Я последовал рекомендациям Алохси о лупе в обратном направлении, потому что это концерт 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

        };

    };
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.