Как правильно перебирать 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.