web-dev-qa-db-fra.com

Quelle est la meilleure façon de parcourir un ensemble d'éléments en JavaScript?

Dans le passé et avec la plupart de mes projets en cours, j'ai tendance à utiliser une boucle for comme celle-ci:

var elements = document.getElementsByTagName('div');
for (var i=0; i<elements.length; i++) {
    doSomething(elements[i]);
}

J'ai entendu dire que l'utilisation d'une boucle "reverse while" est plus rapide mais je n'ai aucun moyen réel de le confirmer:

var elements = document.getElementsByTagName('div'), 
    length = elements.length;

while(length--) {
    doSomething(elements[length]);
}

Qu'est-ce qui est considéré comme la meilleure pratique lorsqu'il s'agit de boucler des éléments en JavaScript, ou tout tableau d'ailleurs?

52
James

Voici une belle forme de boucle que j'utilise souvent. Vous créez la variable itérée à partir de l'instruction for et vous n'avez pas besoin de vérifier la propriété length, qui peut être coûteuse spécialement lors de l'itération via une NodeList. Cependant, vous devez être prudent , vous ne pouvez pas l'utiliser si l'une des valeurs du tableau peut être " fausse ". En pratique, je l'utilise uniquement lors de l'itération sur un tableau d'objets qui ne contient pas de null (comme un NodeList). Mais j'aime son sucre syntaxique.

var list = [{a:1,b:2}, {a:3,b:5}, {a:8,b:2}, {a:4,b:1}, {a:0,b:8}];

for (var i=0, item; item = list[i]; i++) {
  // Look no need to do list[i] in the body of the loop
  console.log("Looping: index ", i, "item" + item);
}

Notez que cela peut également être utilisé pour boucler en arrière (tant que votre liste n'a pas de ['-1'] propriété)

var list = [{a:1,b:2}, {a:3,b:5}, {a:8,b:2}, {a:4,b:1}, {a:0,b:8}];

for (var i = list.length - 1, item; item = list[i]; i--) {
  console.log("Looping: index ", i, "item", item);
}

Mise à jour ES6

for...of vous donne le nom mais pas l'index, disponible depuis ES6

for (let item of list) {
    console.log("Looping: index ", "Sorry!!!", "item" + item);
}
56
Juan Mendes

Notez que dans certains cas, vous avez besoin pour boucler dans l'ordre inverse (mais vous pouvez également utiliser i-- aussi).

Par exemple, quelqu'un voulait utiliser la nouvelle fonction getElementsByClassName pour boucler sur les éléments d'une classe donnée et changer cette classe. Il a constaté qu'un seul élément sur deux avait été modifié (en FF3).
C'est parce que la fonction renvoie une NodeList en direct, qui reflète ainsi les changements dans l'arborescence Dom. Le fait de parcourir la liste dans l'ordre inverse a évité ce problème.

var menus = document.getElementsByClassName("style2");
for (var i = menus.length - 1; i >= 0; i--)
{
  menus[i].className = "style1";
}

En augmentant la progression de l'index, lorsque nous demandons l'index 1, FF inspecte le Dom et saute le premier élément avec style2, qui est le 2e du Dom d'origine, il renvoie donc le 3e élément initial!

27
PhiLho

Au risque de me faire crier dessus, j'obtiendrais une bibliothèque d'aide javascript comme jquery ou prototype ils encapsulent la logique dans les méthodes Nice - les deux ont une méthode .each/itérateur pour le faire - et ils s'efforcent tous les deux de le rendre compatible avec tous les navigateurs

EDIT: Cette réponse a été publiée en 2008. Aujourd'hui, de bien meilleures constructions existent. Ce cas particulier pourrait être résolu avec un .forEach.

Je pense que l'utilisation de la première forme est probablement la voie à suivre, car c'est probablement de loin la structure de boucle la plus courante dans l'univers connu, et puisque je ne crois pas que la boucle inverse vous fait gagner du temps en réalité (toujours en faisant un incrément/décrémentation et une comparaison à chaque itération).

Un code reconnaissable et lisible pour les autres est certainement une bonne chose.

6
Adam Bellaire

J'ai eu un problème très similaire plus tôt avec document.getElementsByClassName (). Je ne savais pas ce qu'était une liste de nodules à l'époque.

var elements = document.getElementsByTagName('div');
for (var i=0; i<elements.length; i++) {
    doSomething(elements[i]);
}

Mon problème était que je m'attendais à ce que les éléments soient un tableau, mais ce n'est pas le cas. La liste nodeliste Document.getElementsByTagName () renvoie est itérable, mais vous ne pouvez pas y appeler de méthodes array.prototype.

Vous pouvez cependant, remplissez un tableau avec des éléments nodelist comme ceci:

var myElements = [];
for (var i=0; i<myNodeList.length; i++) {                               
    var element = myNodeList[i];
    myElements.Push(element);
};

Après cela, vous pouvez vous sentir libre d'appeler .innerHTML ou .style ou quelque chose sur les éléments de votre tableau.

6
Chris Impicciche

Moi aussi je conseille d'utiliser la voie simple (KISS! -)

- mais une optimisation a pu être trouvée, à savoir de ne pas tester la longueur d'un tableau plus d'une fois:

var elements = document.getElementsByTagName('div');
for (var i=0, im=elements.length; im>i; i++) {
    doSomething(elements[i]);
}
5
roenving

Voir aussi mon commentaire sur le test d'Andrew Hedges ...

J'ai juste essayé de lancer un test pour comparer une simple itération, l'optimisation que j'ai introduite et l'inverse do/while, où les éléments d'un tableau ont été testés dans chaque boucle.

Et hélas, pas de surprise, les trois navigateurs que j'ai testés ont eu des résultats très différents, bien que l'itération simple optimisée ait été la plus rapide! -)

Tester:

Un tableau avec 500 000 éléments construit en dehors du test réel, pour chaque itération, la valeur de l'élément de tableau spécifique est révélée.

Test exécuté 10 fois.

IE6:

Résultats:

Simple: 984.922.937.984.891.907.906.891.906.906

Moyenne: 923,40 ms.

Optimisé: 766.766.844.797.750.750.765.765.766.766

Moyenne: 773,50 ms.

Marche arrière inversée: 3375,1328,1516,1344,1375,1406,1688,1344,1297,1265

Moyenne: 1593.80 ms. (Notez un résultat particulièrement gênant)

Opera 9.52:

Résultats:

Simple: 344 343 344 359 343 359 344 359 359 359

Moyenne: 351,30 ms.

Optimisé: 281 297 297 297 297 281 281 297 281 281

Moyenne: 289,00 ms

Marche arrière inversée: 391 407 391 391 500 500 407 407 406 406 406

Moyenne: 411,20 ms.

FireFox 3.0.1:

Résultats:

Simple: 278,251,259,245,243,242,259,246,247,256

Moyenne: 252,60 ms.

Optimisé: 267,222,223,226,223,230,221,231,224,230

Moyenne: 229,70 ms.

Marche arrière inversée: 414.381.389.383.388.389.381.387.400.379

Moyenne: 389,10 ms.

4
roenving

La forme de boucle fournie par Juan Mendez est très utile et pratique, je l'ai modifiée un peu, de sorte que maintenant cela fonctionne avec - des chaînes fausses, nulles, nulles et vides aussi.

var items = [
    true,
    false,
    null,
    0,
    ""
];

for(var i = 0, item; (item = items[i]) !== undefined; i++)
{
    console.log("Index: " + i + "; Value: " + item);
}
3
user4416767

Je sais que vous ne voulez pas entendre cela, mais: je considère que la meilleure pratique est la plus lisible dans ce cas. Tant que la boucle ne compte pas d'ici à la lune, le gain de performance ne sera pas assez élevé.

1
Georgi

Je sais que cette question est ancienne - mais voici une autre solution extrêmement simple ...

var elements = Array.from(document.querySelectorAll("div"));

Ensuite, il peut être utilisé comme n'importe quel tableau standard.

0
Dustin Halstead

Je préfère la boucle for car elle est plus lisible. Un bouclage de longueur à 0 serait plus efficace qu'un bouclage de 0 à longueur. Et utiliser une boucle while inversée est plus efficace qu'une boucle foor comme vous l'avez dit. Je n'ai plus le lien vers la page avec les résultats de comparaison mais je me souviens que la différence variait selon les navigateurs. Pour certains navigateurs, la boucle while inversée était deux fois plus rapide. Cependant, cela ne fait aucune différence si vous bouclez de "petits" tableaux. Dans votre exemple, la longueur des éléments sera "petite"

0
Gene

Je pense que vous avez deux alternatives. Pour les éléments dom tels que jQuery et les frameworks similaires, donnez-vous une bonne méthode d'itération. La deuxième approche est la boucle for.

0
David Robbins