Comment parcourir correctement getElementsByClassName

102

Je suis débutant en Javascript.

J'ouvre la page Web via le window.onload, je dois trouver un tas d'éléments par leur nom de classe ( slide) et les redistribuer dans différents nœuds en fonction d'une logique. J'ai une fonction Distribute(element)qui prend un élément comme entrée et fait la distribution. Je veux faire quelque chose comme ça (comme indiqué par exemple ici ou ici ):

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

cependant cela ne fait pas la magie pour moi, car getElementsByClassNamene renvoie pas réellement de tableau, mais un NodeList, qui est ...

... c'est ma spéculation ...

... en cours de modification à l'intérieur de la fonction Distribute(l'arborescence DOM est modifiée à l'intérieur de cette fonction et le clonage de certains nœuds se produit). For-eachla structure en boucle n'aide pas non plus.

Les diapositives variables agissent de manière vraiment non déterministe, à chaque itération, elles changent énormément la longueur et l'ordre des éléments.

Quelle est la bonne façon d'itérer dans NodeList dans mon cas? Je pensais remplir un tableau temporaire, mais je ne sais pas comment faire cela ...

ÉDITER:

fait important que j'ai oublié de mentionner est qu'il pourrait y avoir une diapositive dans une autre, c'est en fait ce qui change la slidesvariable comme je viens de le découvrir grâce à l'utilisateur Alohci .

La solution pour moi était de cloner chaque élément dans un tableau d' abord et passer le tableau ono par un dans la Distribute()suite.

Kupto
la source
3
C'est en fait la façon de le faire, vous devez donc gâcher autre chose!
adeneo
la Distribute()fonction est trop longue et complexe pour être copiée ici, mais je suis certain que je change la structure DOM à l'intérieur, j'y duplique (clonage) également des éléments. Quand je le débogue, je peux voir les slideschangements de variable chaque fois qu'il est passé à l'intérieur.
Kupto
Cela ne change pas à moins que vous ne le changiez quelque part.
adeneo
5
Je crois que cela getElementsByClassName()renvoie un live nodeList, donc à mesure que les éléments avec cette classe sont ajoutés, la longueur de la période nodeListsur laquelle vous itérez change.
David dit de réintégrer Monica le
2
@ Kupto-boucler à l'envers résout souvent ce genre de problème, où la fonction Distribute supprime ou déplace l'élément de sorte qu'il ne corresponde plus à l'appel getElementsByClassName, pour la raison que David Thomas donne.
Alohci

Réponses:

131

Selon MDN, la façon de récupérer un élément à partir d'un NodeListest:

nodeItem = nodeList.item(index)

Donc:

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

Je n'ai pas essayé moi-même (la forboucle normale a toujours fonctionné pour moi), mais essayez-le .

Albert Xing
la source
C'est la bonne solution, à moins que vous n'essayiez de rechercher et de modifier des éléments qui ont une même classe et qui sont l'un dans l'autre. J'ai expliqué ma solution de contournement en éditant ma question.
Kupto
Bien sûr, je n'en ai pas tenu compte.
Albert Xing
Pourquoi est-ce ainsi, si je peux demander? Pourquoi n'est-il pas implémenté pour que vous puissiez itérer sur les nœuds de cette manière for(var el in document.getElementsByClassName("foo")){}?
Nearoo
3
for ... ofvous permet d'itérer sur NodeList maintenant comme dans for (slide of slides) Distribute(slide). La prise en charge du navigateur est inégale, mais si vous transpilez, elle for ... ofsera convertie, mais NodeList.forEachpas.
Mr5o1
69

Si vous utilisez le nouveau querySelectorAll, vous pouvez appeler forEach directement.

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

Par le commentaire ci-dessous. Les listes de nœuds n'ont pas de fonction forEach.

Si vous l'utilisez avec babel, vous pouvez ajouter Array.fromet cela convertira les listes sans nœuds en un tableau forEach. Array.fromne fonctionne pas de manière native dans les navigateurs ci-dessous et y compris IE 11.

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

Lors de notre rencontre d'hier soir, j'ai découvert une autre façon de gérer les listes de nœuds n'ayant pas forEach

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

Prise en charge du navigateur pour [...]

Affichage sous forme de liste de nœuds

Affichage sous forme de liste de nœuds

Affichage sous forme de tableau

Affichage sous forme de tableau

styks
la source
4
Gotcha sur ceci est que nodeLists n'a pas de fonction forEach sur eux dans chaque navigateur. Si vous êtes prêt à bricoler des prototypes, c'est assez simple à faire:if ( !NodeList.prototype.forEach ) {NodeList.prototype.forEach = Array.prototype.forEach;}
joshcanhelp
Solution élégante si je combine votre réponse avec le commentaire de @joshcanhelp. Merci :) Bien sûr, cela ne mènera qu'à un avantage de ligne avec plusieurs boucles.
yarwest
1
Vous devez éviter cela car cela peut ne pas fonctionner sur tous les navigateurs. Voici une solution de contournement simple que j'utilise et qui semble fonctionner parfaitement partout: css-tricks.com/snippets/javascript/…
tixastronauta
Je pense que vous vouliez dire[...document.getElementsByClassName('.edit')].forEach(function(button) {
wp-overwatch.com
@ wp-overwatch.com le point n'est pas nécessaire dans le nom de la classe. La version correcte devrait être:[...document.getElementsByClassName('edit')].forEach(function(button) {
MXT
11

Vous pouvez toujours utiliser des méthodes de tableau:

var slides = getElementsByClassName("slide");
Array.prototype.forEach.call(slides, function(slide, index) {
    Distribute(slides.item(index));
});
Andrew
la source
très belle et belle réponse, merci beaucoup!
Olga Farber
1
Qu'est-ce que distribuer?
lesolorzanov
7

J'ai suivi la recommandation d' Alohci de boucler à l'envers car c'est un live nodeList. Voici ce que j'ai fait pour ceux qui sont curieux ...

  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);
  }
ayjay
la source
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>
Kushal Desai
la source
0

J'ai eu un problème similaire avec l'itération et j'ai atterri ici. Peut-être que quelqu'un d'autre fait aussi la même erreur que moi.

Dans mon cas, le sélecteur n'était pas du tout le problème. Le problème était que j'avais gâché le code javascript: j'avais une boucle et une sous-boucle. La sous-boucle était également utilisée icomme compteur, au lieu de j, donc parce que la sous-boucle remplaçait la valeur de ide la boucle principale, celle-ci n'a jamais atteint la deuxième itération.

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

        };

    };
J0ANMM
la source