web-dev-qa-db-fra.com

Pourquoi nodelist n'a-t-il pas forEach?

Je travaillais sur un court script pour changer le texte intérieur des éléments <abbr>, mais j'ai constaté que nodelist n'a pas de méthode forEach. Je sais que nodelist n'hérite pas de Array, mais ne semble-t-il pas que forEach serait une méthode utile? Existe-t-il un problème d'implémentation dont je ne suis pas au courant qui empêche d'ajouter forEach à nodelist?

Remarque: Je suis conscient que Dojo et jQuery ont tous les deux forEach sous une forme quelconque pour leurs listes de nœuds. Je ne peux pas utiliser non plus en raison de limitations.

79
Snakes and Coffee

NodeList a maintenant forEach () dans tous les principaux navigateurs

Voir nodeList forEach () on MDN .

Réponse originale

Aucune de ces réponses n'explique pourquoi NodeList [] n'hérite pas de Array, lui permettant ainsi d'avoir forEach et tout le reste.

La réponse est trouvée sur ce fil d'es-discussion . En bref, ça casse le web:

Le problème était un code qui supposait à tort que instanceof signifiait que l'instance était un tableau associé à Array.prototype.concat.

Il y avait un bogue dans la bibliothèque de fermeture de Google qui causait l'échec de presque toutes les applications de Google pour cette raison. La bibliothèque a été mise à jour dès que cela a été trouvé, mais il se peut qu'il y ait encore du code faisant la même hypothèse incorrecte en combinaison avec concat.

C'est-à-dire qu'un code a fait quelque chose comme

if (x instanceof Array) {
  otherArray.concat(x);
} else {
  doSomethingElseWith(x);
}

Cependant, concat traitera les "vrais" tableaux (et non l'instance de Array) différemment des autres objets:

[1, 2, 3].concat([4, 5, 6]) // [1, 2, 3, 4, 5, 6]
[1, 2, 3].concat(4) // [1, 2, 3, 4]

cela signifie donc que le code ci-dessus a éclaté lorsque x était une liste de noeuds, car avant de suivre le chemin doSomethingElseWith(x), puis de suivre le chemin otherArray.concat(x), ce qui avait un effet étrange puisque x n'était pas un véritable tableau.

Pendant un certain temps, il a été proposé de créer une classe Elements qui serait une véritable sous-classe de Array et qui serait utilisée comme "nouvelle liste de noeuds". Cependant, cela était retiré de la norme DOM , du moins pour le moment, car sa mise en œuvre n’était pas encore réalisable pour diverses raisons techniques et liées aux spécifications.

80
Domenic

Tu peux faire

Array.prototype.forEach.call (nodeList, function (node) {

    // Your code here.

} );
48
akuhn

Vous pouvez envisager de créer un nouveau tableau de nœuds. 

  var nodeList = document.getElementsByTagName('div'),

      nodes = Array.prototype.slice.call(nodeList,0); 

  // nodes is an array now.
  nodes.forEach(function(node){ 

       // do your stuff here.  

  });

Remarque: Ceci est juste une liste/tableau de références de nœuds que nous créons ici, pas de nœuds en double.

  nodes[0] === nodeList[0] // will be true
31
sbr

Ne dites jamais jamais, c'est 2016 et l'objet NodeList a implémenté une méthode forEach dans le dernier chrome (v52.0.2743.116).

Il est trop tôt pour l'utiliser en production car les autres navigateurs ne le supportent pas encore (testé 49 FF), mais je suppose que cela sera bientôt normalisé.

18
maioman

En bref, c'est un conflit de conception pour mettre en œuvre cette méthode.

De MDN:

Pourquoi ne puis-je pas utiliser forEach ou mapper sur une liste de noeuds?

NodeList s'utilise beaucoup comme des tableaux et il serait tentant de utilisez les méthodes Array.prototype sur eux. Ceci est cependant impossible.

JavaScript a un mécanisme d'héritage basé sur des prototypes. Tableau les instances héritent des méthodes de tableau (telles que forEach ou map) car leurs La chaîne prototype ressemble à ce qui suit:

myArray --> Array.prototype --> Object.prototype --> null (la chaîne de prototypes d'un objet peut être obtenue en appelant plusieurs fois Object.getPrototypeOf)

forEach, map et les likes sont des propriétés propres à Array.prototype objet.

Contrairement aux tableaux, la chaîne de prototypes NodeList se présente comme suit:

myNodeList --> NodeList.prototype --> Object.prototype --> null

NodeList.prototype contient la méthode de l'élément, mais aucun des fichiers Méthodes Array.prototype, elles ne peuvent donc pas être utilisées sur NodeLists.

Source: https://developer.mozilla.org/en-US/docs/DOM/NodeList (descendez jusqu'à Pourquoi ne puis-je pas utiliser forEach ou mapper sur une NodeList?)

15
Matt Lo

Si vous souhaitez utiliser forEach sur NodeList, copiez simplement cette fonction à partir de Array:

NodeList.prototype.forEach = Array.prototype.forEach;

C'est tout, vous pouvez maintenant l'utiliser de la même manière que vous le feriez pour Array:

document.querySelectorAll('td').forEach(function(o){
   o.innerHTML = 'text';
});
13
AlexTR

Dans ES2015, vous pouvez maintenant utiliser la méthode forEach pour la liste de noeuds.

document.querySelectorAll('abbr').forEach( el => console.log(el));

Voir le MDN Link

Toutefois, si vous souhaitez utiliser des collections HTML ou d'autres objets de type tableau, vous pouvez utiliser la méthode Array.from() dans es2015. Cette méthode prend un objet semblable à un tableau ou itératif (y compris nodeList, Collections HTML, chaînes, etc.) et renvoie une nouvelle instance Array. Vous pouvez l'utiliser comme ceci:

const elements = document.getElementsByTagName('abbr');
Array.from(elements).forEach( el => console.log(el));

Comme la méthode Array.from() est modulable, vous pouvez l’utiliser dans le code es5 comme ceci

var elements = document.getElementsByTagName('abbr');
Array.from(elements).forEach( function(el) {
    console.log(el);
});

Pour plus de détails, voir la page MDN .

Pour vérifier support actuel du navigateur .

OU

un autre moyen es2015 consiste à utiliser l'opérateur spread.

[...document.querySelectorAll('abbr')].forEach( el => console.log(el));

Opérateur de propagation MDN

Spread Operator - Prise en charge du navigateur

4

Ma solution: 

//foreach for nodeList
NodeList.prototype.forEach = Array.prototype.forEach;
//foreach for HTML collection(getElementsByClassName etc.)
HTMLCollection.prototype.forEach = Array.prototype.forEach;
2
Bakos Bence

NodeList fait partie de l'API DOM. Examinez les liaisons ECMAScript qui s'appliquent également à JavaScript. http://www.w3.org/TR/DOM-Level-2-Core/ecma-script-binding.html . NodeList et une propriété de longueur en lecture seule et une fonction item (index) pour renvoyer un nœud. 

La réponse est, vous devez itérer. Il n'y a pas d'alternative. Foreach ne fonctionnera pas . Je travaille avec des liaisons API Java DOM et ai le même problème. 

Recherchez dans le MDN la spécification NodeList.forEach .

NodeList.forEach(function(item, index, nodeList) {
    // code block here
});

Dans IE, utilisez la réponse de akuhn :

[].forEach.call(NodeList, function(item, index, array) {
    // code block here
});
0
VesperX