Jak poprawnie wykonać iterację przez getElementsByClassName


102

Jestem początkującym użytkownikiem Javascript.

Inicjuję stronę internetową za pośrednictwem window.onload, muszę znaleźć grupę elementów według nazwy ich klasy ( slide) i ponownie rozdzielić je na różne węzły w oparciu o jakąś logikę. Mam funkcję, Distribute(element)która przyjmuje element jako dane wejściowe i wykonuje dystrybucję. Chcę zrobić coś takiego (jak opisano na przykład tutaj lub tutaj ):

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

jednak nie robi to dla mnie magii, ponieważ getElementsByClassNametak naprawdę nie zwraca tablicy, ale a NodeList, czyli ...

... to moja spekulacja ...

... zmieniane wewnątrz funkcji Distribute(drzewo DOM jest zmieniane wewnątrz tej funkcji i zachodzi klonowanie niektórych węzłów). For-eachstruktura pętli też nie pomaga.

Zmienne slajdy działają naprawdę nieokreślonistycznie, przez każdą iterację gwałtownie zmienia swoją długość i kolejność elementów.

Jaki jest właściwy sposób iteracji przez NodeList w moim przypadku? Myślałem o wypełnieniu tymczasowej tablicy, ale nie jestem pewien, jak to zrobić ...

EDYTOWAĆ:

Ważnym faktem, o którym zapomniałem wspomnieć, jest to, że jeden slajd może znajdować się wewnątrz drugiego, tak naprawdę zmienia slideszmienną, o czym właśnie dowiedziałem się dzięki użytkownikowi Alohci .

Rozwiązaniem dla mnie było najpierw sklonowanie każdego elementu do tablicy, a następnie przekazanie tablicy on-by-one Distribute().


3
Właściwie to jest sposób na zrobienie tego, więc musisz coś zepsuć!
adeneo,

Distribute()funkcją jest długie i skomplikowane, aby być kopiowane tutaj, ale jestem pewien, że jestem zmieniając wnętrze struktury DOM, ja też powielanie (klonowanie) elementy tam. Kiedy go debuguję, widzę, jak zmienna slideszmienia się za każdym razem, gdy jest przekazywana do środka.
Kupto

Nie zmienia się, chyba że gdzieś go zmienisz.
adeneo,

5
Uważam, że getElementsByClassName()zwraca to na żywo nodeList, więc elementy z tą klasą są dodawane do długości zmian, nodeListpo których iterujesz.
David mówi, żeby przywrócić Monice

2
@ Kupto- zapętlenie w odwrotnej kolejności często rozwiązuje tego rodzaju problem, w którym funkcja Distribute usuwa lub przesuwa element w taki sposób, że nie pasuje on już do wywołania getElementsByClassName, z powodu podanego przez Davida Thomas.
Alohci,

Odpowiedzi:


131

Według MDN sposób na pobranie przedmiotu z pliku NodeList jest:

nodeItem = nodeList.item(index)

A zatem:

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

Sam tego nie próbowałem (normalna forpętla zawsze działała), ale daj jej szansę.


To jest właściwe rozwiązanie, chyba że spróbujesz wyszukać i zmienić elementy, które mają tę samą klasę i znajdują się w sobie. Wyjaśniłem obejście problemu w edycji na moje pytanie.
Kupto

Jasne, nie wziąłem tego pod uwagę.
Albert Xing,

Dlaczego tak jest, jeśli mogę zapytać? Dlaczego nie jest zaimplementowany, abyś mógł iterować po węzłach w ten sposób for(var el in document.getElementsByClassName("foo")){}?
Nearoo

3
for ... ofpozwala na iterację po NodeList teraz, jak w for (slide of slides) Distribute(slide). Obsługa przeglądarki jest niejednolita, ale jeśli transpilujesz, for ... ofzostanie przekonwertowana, ale NodeList.forEachnie.
Mr5o1

69

Jeśli używasz nowego querySelectorAll, możesz bezpośrednio wywołać forEach.

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

Zgodnie z komentarzem poniżej. nodeLists nie mają funkcji forEach.

Jeśli używasz tego z babel, możesz dodać Array.fromi przekonwertować listy bez węzłów na tablicę forEach. Array.fromnie działa natywnie w przeglądarkach poniżej, w tym w IE 11.

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

Na naszym zeszłym spotkaniu odkryłem inny sposób obsługi list węzłów, które nie mają funkcji forEach

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

Obsługa przeglądarek dla [...]

Wyświetlane jako lista węzłów

Wyświetlane jako lista węzłów

Wyświetlane jako tablica

Wyświetlane jako tablica


4
Rozumiem, że nodeLists nie mają na sobie funkcji forEach w każdej przeglądarce. Jeśli chcesz majstrować przy prototypach, jest to dość proste:if ( !NodeList.prototype.forEach ) {NodeList.prototype.forEach = Array.prototype.forEach;}
joshcanhelp

Eleganckie rozwiązanie, jeśli połączę Twoją odpowiedź z komentarzem @joshcanhelp. Dzięki :) Oczywiście doprowadzi to tylko do przewagi linii z wieloma pętlami.
yarwest

1
Należy tego unikać, ponieważ może to nie działać we wszystkich przeglądarkach. Oto proste obejście, którego używam i wydaje się działać idealnie wszędzie: css-tricks.com/snippets/javascript/ ...
tixastronauta

Myślę, że miałeś na myśli[...document.getElementsByClassName('.edit')].forEach(function(button) {
wp-overwatch.com

@ wp-overwatch.com kropka nie jest potrzebna w nazwie klasy. Prawidłowa wersja to:[...document.getElementsByClassName('edit')].forEach(function(button) {
MXT

11

Zawsze możesz użyć metod tablicowych:

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

bardzo miła i piękna odpowiedź, bardzo dziękuję!
Olga Farber

1
Co to jest rozpowszechnianie?
lesolorzanov

7

Postępowałem zgodnie z zaleceniem Alohciego , by robić pętlę w odwrotnej kolejności, ponieważ to koncert nodeList. Oto, co zrobiłem dla tych, którzy są ciekawi ...

  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

Miałem podobny problem z iteracją i wylądowałem tutaj. Może ktoś inny również popełnia ten sam błąd, co ja.

W moim przypadku selektor wcale nie był problemem. Problem polegał na tym, że zepsułem kod javascript: miałem pętlę i subloop. Podpętla była również używana ijako licznik zamiast j, więc ponieważ pętla podrzędna przesłaniała wartość ipętli głównej, ta nigdy nie przeszła do drugiej iteracji.

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

        };

    };
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.