Come iterare correttamente attraverso getElementsByClassName


101

Sono un principiante Javascript.

Sto iniziando la pagina web tramite window.onload, devo trovare un mucchio di elementi con il loro nome di classe ( slide) e ridistribuirli in diversi nodi in base a una logica. Ho una funzione Distribute(element)che prende un elemento come input e fa la distribuzione. Voglio fare qualcosa di simile (come descritto ad esempio qui o qui ):

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

tuttavia questo non fa la magia per me, perché in getElementsByClassNamerealtà non restituisce array, ma a NodeList, che è ...

... questa è la mia speculazione ...

... viene modificato all'interno della funzione Distribute(l'albero DOM viene modificato all'interno di questa funzione e viene eseguita la clonazione di alcuni nodi). For-eachneanche la struttura del loop aiuta.

Le diapositive variabili agiscono in modo davvero indeterministico, attraverso ogni iterazione cambia selvaggiamente la lunghezza e l'ordine degli elementi.

Qual è il modo corretto per scorrere NodeList nel mio caso? Stavo pensando di riempire un array temporaneo, ma non sono sicuro di come farlo ...

MODIFICARE:

Fatto importante che ho dimenticato di menzionare è che potrebbe esserci una diapositiva dentro un'altra, questo è in realtà ciò che cambia la slidesvariabile come ho appena scoperto grazie all'utente Alohci .

La soluzione per me è stata quella di clonare prima ogni elemento in un array e poi passare l'array uno alla volta Distribute().


3
Questo è effettivamente il modo per farlo, quindi devi incasinare qualcos'altro!
adeneo

la Distribute()funzione è troppo lunga e complessa per essere copiata qui, ma sono certo che sto cambiando la struttura DOM all'interno, sto anche duplicando (clonando) elementi lì. Quando eseguo il debug, posso vedere le slidesmodifiche della variabile ogni volta che viene passata all'interno.
Kupto

Non cambia a meno che tu non lo cambi effettivamente da qualche parte.
adeneo

5
Credo che getElementsByClassName()restituisca un live nodeList, quindi quando gli elementi con quella classe vengono aggiunti la lunghezza del nodeListsu cui stai iterando le modifiche.
David dice di reintegrare Monica il

2
@ Kupto- il looping al contrario spesso risolve questo tipo di problema, in cui la funzione Distribute rimuove o sposta l'elemento in modo tale che non corrisponda più alla chiamata getElementsByClassName, per il motivo fornito da David Thomas.
Alohci

Risposte:


130

Secondo MDN, il modo per recuperare un oggetto da un NodeListè:

nodeItem = nodeList.item(index)

Quindi:

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

Non l'ho provato da solo (il forciclo normale ha sempre funzionato per me), ma provalo.


Questa è la soluzione giusta, a meno che non si cerchi di cercare e modificare elementi che hanno una stessa classe e sono uno dentro l'altro. Ho spiegato la mia soluzione alternativa in modifica alla mia domanda.
Kupto

Certo, non ne ho tenuto conto.
Albert Xing

Perché è in questo modo, se posso chiedere? Perché non è implementato in modo da essere in grado di iterare sui nodi in questo modo for(var el in document.getElementsByClassName("foo")){}?
Nearoo

3
for ... ofti permette di iterare su NodeList ora come in for (slide of slides) Distribute(slide). Il supporto del browser è frammentario, ma se stai transpiling for ... ofverrà convertito, ma NodeList.forEachnon lo farebbe.
Mr5o1

68

Se usi il nuovo querySelectorAll puoi chiamare forEach direttamente.

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

Per il commento qui sotto. nodeLists non ha una funzione forEach.

Se lo usi con babel puoi aggiungere Array.frome convertirà gli elenchi non di nodi in un array forEach. Array.fromnon funziona in modo nativo nei browser seguenti e incluso IE 11.

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

Al nostro incontro di ieri sera ho scoperto un altro modo per gestire gli elenchi di nodi che non hanno forEach

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

Supporto browser per [...]

Mostrato come elenco dei nodi

Mostrato come elenco dei nodi

Mostrato come array

Mostrato come array


4
Il problema è che nodeLists non ha una funzione forEach in ogni browser. Se sei disposto ad armeggiare con i prototipi, è abbastanza semplice da fare:if ( !NodeList.prototype.forEach ) {NodeList.prototype.forEach = Array.prototype.forEach;}
joshcanhelp

Soluzione elegante se combino la tua risposta con il commento di @joshcanhelp. Grazie :) Ovviamente questo porterà solo a un vantaggio di linea con più loop.
yarwest

1
Dovresti evitarlo perché potrebbe non funzionare su tutti i browser. Ecco una semplice soluzione alternativa che uso e sembra funzionare perfettamente ovunque: css-tricks.com/snippets/javascript/…
tixastronauta

Penso che volevi dire[...document.getElementsByClassName('.edit')].forEach(function(button) {
wp-overwatch.com

@ wp-overwatch.com il punto non è necessario in classname. La versione corretta dovrebbe essere:[...document.getElementsByClassName('edit')].forEach(function(button) {
MXT

11

Puoi sempre usare metodi array:

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

risposta molto bella e bella, grazie mille!
Olga Farber

1
Cos'è Distribuisci?
lesolorzanov

7

Ho seguito la raccomandazione di Alohci di girare in loop al contrario perché è un live nodeList. Ecco cosa ho fatto per chi è curioso ...

  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

Ho avuto un problema simile con l'iterazione e sono atterrato qui. Forse anche qualcun altro sta facendo lo stesso errore che ho fatto io.

Nel mio caso, il selettore non era affatto il problema. Il problema era che avevo sbagliato il codice javascript: avevo un loop e un subloop. Anche il subloop stava usando icome contatore, invece di j, quindi poiché il subloop stava sovrascrivendo il valore di idel ciclo principale, questo non è mai arrivato alla seconda iterazione.

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

        };

    };
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.