Cómo iterar correctamente a través de getElementsByClassName


101

Soy un principiante de Javascript.

Estoy ingresando a la página web a través de window.onload, tengo que encontrar un montón de elementos por su nombre de clase ( slide) y redistribuirlos en diferentes nodos según la lógica. Tengo una función Distribute(element)que toma un elemento como entrada y realiza la distribución. Quiero hacer algo como esto (como se describe, por ejemplo, aquí o aquí ):

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

sin embargo, esto no me hace la magia, porque en getElementsByClassNamerealidad no devuelve una matriz, sino a NodeList, que es ...

... esta es mi especulación ...

... se cambia dentro de la función Distribute(el árbol DOM se cambia dentro de esta función y se produce la clonación de ciertos nodos). For-eachLa estructura de bucle tampoco ayuda.

Las diapositivas variables actúan de manera no determinista, a través de cada iteración cambia su longitud y orden de los elementos de manera salvaje.

¿Cuál es la forma correcta de iterar a través de NodeList en mi caso? Estaba pensando en llenar una matriz temporal, pero no estoy seguro de cómo hacerlo ...

EDITAR:

Un hecho importante que olvidé mencionar es que puede haber una diapositiva dentro de otra, esto es en realidad lo que cambia la slidesvariable como acabo de descubrir gracias al usuario Alohci .

La solución para mí fue clonar cada elemento en una matriz primero y pasar la matriz uno por uno Distribute()después.


3
Esta es realmente la forma de hacerlo, ¡así que debes estar arruinando algo más!
adeneo

la Distribute()función es demasiado larga y compleja para copiarla aquí, pero estoy seguro de que estoy cambiando la estructura DOM interna, también estoy duplicando (clonando) elementos allí. Cuando lo depuro, puedo ver que la variable slidescambia cada vez que se pasa al interior.
Kupto

No cambia a menos que lo cambie en alguna parte.
adeneo

5
Creo que getElementsByClassName()devuelve un live nodeList, por lo que a medida que los elementos con esa clase se agregan, la longitud nodeListsobre la que está iterando cambia.
David dice reinstalar a Monica

2
@ Kupto- el bucle inverso a menudo resuelve este tipo de problema, donde la función Distribute elimina o mueve el elemento de tal manera que ya no coincide con la llamada getElementsByClassName, por la razón que da David Thomas.
Alohci

Respuestas:


130

Según MDN, la forma de recuperar un artículo de un NodeListes:

nodeItem = nodeList.item(index)

Así:

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

No lo he probado yo mismo (el forbucle normal siempre me ha funcionado), pero pruébalo.


Esta es la solución correcta, a menos que intente buscar y cambiar elementos que tienen la misma clase y están dentro de otros. Expliqué mi solución alternativa en la edición de mi pregunta.
Kupto

Claro, no lo tomé en cuenta.
Albert Xing

¿Por qué es así, si puedo preguntar? ¿Por qué no está implementado para que pueda iterar sobre los nodos de esta manera for(var el in document.getElementsByClassName("foo")){}?
Nearoo

3
for ... ofle permite iterar sobre NodeList ahora como en for (slide of slides) Distribute(slide). La compatibilidad con el navegador es irregular, pero si está transpilando for ... of, se convertirá, pero NodeList.forEachno lo hará.
Mr5o1

68

Si usa el nuevo querySelectorAll, puede llamar a forEach directamente.

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

Según el comentario a continuación. nodeLists no tiene una función forEach.

Si usa esto con babel, puede agregar Array.fromy convertirá listas de nodos en una matriz forEach. Array.fromno funciona de forma nativa en los navegadores siguientes e incluido IE 11.

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

En nuestra reunión de anoche, descubrí otra forma de manejar las listas de nodos sin tener forEach

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

Soporte de navegador para [...]

Mostrando como lista de nodos

Mostrando como lista de nodos

Mostrando como matriz

Mostrando como matriz


4
Lo que entendí es que las listas de nodos no tienen una función forEach en todos los navegadores. Si está dispuesto a jugar con los prototipos, es bastante simple:if ( !NodeList.prototype.forEach ) {NodeList.prototype.forEach = Array.prototype.forEach;}
joshcanhelp

Solución elegante si combino tu respuesta con el comentario de @joshcanhelp. Gracias :) Por supuesto, esto solo conducirá a una ventaja de línea con múltiples bucles.
yarwest

1
Debe evitar esto porque es posible que no funcione en todos los navegadores. Aquí hay una solución alternativa simple que uso y parece funcionar perfectamente en todas partes: css-tricks.com/snippets/javascript/…
tixastronauta

Creo que quisiste decir[...document.getElementsByClassName('.edit')].forEach(function(button) {
wp-overwatch.com

@ wp-overwatch.com el punto no es necesario en classname. La versión correcta debería ser:[...document.getElementsByClassName('edit')].forEach(function(button) {
MXT

11

Siempre puedes usar métodos de matriz:

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

muy bonita y bonita respuesta, muchas gracias!
Olga Farber

1
¿Qué es Distribuir?
lesolorzanov

7

Seguí Alohci recomendación del bucle en sentido inverso, porque es un vivo 's nodeList. Esto es lo que hice por aquellos que tienen curiosidad ...

  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

Tuve un problema similar con la iteración y aterricé aquí. Quizás alguien más también esté cometiendo el mismo error que yo.

En mi caso, el selector no fue el problema en absoluto. El problema era que había estropeado el código javascript: tenía un bucle y un subloop. El subloop también se estaba usando icomo contador, en lugar de j, así que debido a que el subloop estaba anulando el valor del ibucle principal, este nunca llegó a la segunda iteración.

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

        };

    };
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.