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

91

Je travaillais sur un court script pour changer <abbr>le texte intérieur des éléments, mais nodelistj'ai trouvé qu'il n'avait pas de forEachméthode. Je sais que nodelistcela n'hérite pas de Array, mais ne semble-t-il pas forEachêtre une méthode utile à avoir? Y at - il un problème de mise en œuvre particulière , je ne suis pas au courant de cela empêche l' ajout forEachde nodelist?

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

Serpents et café
la source
6
Bonjour du futur! nodeList a forEach depuis ES6 .
Blaise du

Réponses:

94

NodeList a maintenant forEach () dans tous les principaux navigateurs

Voir nodeList forEach () sur MDN .

Réponse originale

Aucune de ces réponses n'explique pourquoi NodeList n'hérite pas de Array, ce qui lui permet d'avoir forEachet tout le reste.

La réponse se trouve sur ce fil de discussion es-discuter . Bref, ça casse le web:

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

Il y avait un bogue dans la bibliothèque de fermeture de Google qui a causé l'échec de presque toutes les applications de Google à cause de cela. La bibliothèque a été mise à jour dès que cela a été trouvé, mais il peut encore y avoir du code là-bas qui fait la même hypothèse incorrecte en combinaison avec concat.

Autrement dit, un code a fait quelque chose comme

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

Cependant, concattraitera les tableaux "réels" (et non les instances 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]

donc cela signifie que le code ci-dessus s'est cassé quand xétait une NodeList, car avant de descendre dans le doSomethingElseWith(x)chemin, alors qu'après, il a otherArray.concat(x)emprunté le chemin, ce qui a fait quelque chose de bizarre puisque ce xn'était pas un vrai tableau.

Pendant un certain temps, il y avait une proposition pour une Elementsclasse qui était une vraie sous-classe d'Array, et serait utilisée comme "la nouvelle NodeList". Cependant, cela a été supprimé du standard DOM , du moins pour le moment, car il n'était pas encore possible de l'implémenter pour diverses raisons techniques et liées aux spécifications.

Domenic
la source
11
Cela me semble être un mauvais appel. Sérieusement, je pense que c'est la bonne décision de casser des choses de temps en temps, en particulier si cela signifie que nous avons des API saines pour l'avenir. En outre, ce n'est pas comme si le Web était même proche d'être une plate-forme stable, les gens sont habitués à ce que leur javascript de 2 ans ne fonctionne plus comme prévu ..
JoyalToTheWorld
3
"Breaks the web"! = "Breaks Google stuff"
Matt
Pourquoi ne pas simplement emprunter la méthode forEach à Array.prototype? Par exemple, au lieu d'ajouter Array comme prototype, faites simplement ceci.forEach = Array.prototype.forEach dans le constructeur, ou même implémentez simplement forEach uniquement pour NodeList?
PopKernel
1
Remarque; cette mise NodeList now has forEach() in all major browsersà jour semble impliquer que IE n'est pas un navigateur majeur. J'espère que c'est vrai pour certaines personnes, mais ce n'est pas vrai pour moi (encore).
Graham P Heath
58

Tu peux faire

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

    // Your code here.

} );
Akuhn
la source
3
Array.prototype.forEach.callpeut être raccourci vers[].forEach.call
CodeBrauer
7
@CodeBrauer: ce n'est pas seulement raccourcir Array.prototype.forEach.call, c'est créer un tableau vide et utiliser sa forEachméthode.
Paul D.Waite
34

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 / un 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
sbr
la source
22
Ou faites simplement Array.prototype.forEach.call(nodeList, fun).
akuhn
Je suggère également la fonction aliasing forEach tel que: var forEach = Array.prototype.forEach.call(nodeList, callback);. Maintenant, vous pouvez simplement appelerforEach(nodeList, callback);
Andrew Craswell
19

Ne dites jamais jamais, nous sommes en 2016 et l' NodeListobjet a implémenté une forEachméthode dans le dernier chrome (v52.0.2743.116).

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

Maioman
la source
2
Opera le prend également en charge et le support sera ajouté dans la v50 de Firefox, dont la sortie est prévue le 15/11/16.
Shaggy
1
Bien qu'implémenté, il ne fait partie d'aucune norme. Il est toujours préférable de faire Array.prototype.slice.call(nodelist).forEach(…)ce qui est standard et fonctionne dans les anciens navigateurs.
Nate
17

En bref, c'est un conflit de conception pour implémenter cette méthode.

De MDN:

Pourquoi ne puis-je pas utiliser forEach ou map sur une liste de nœuds?

NodeList sont très utilisés comme des tableaux et il serait tentant d'utiliser les méthodes Array.prototype sur eux. Ceci est cependant impossible.

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

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

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

Contrairement aux tableaux, la chaîne de prototypes NodeList ressemble à ce qui suit:

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

NodeList.prototype contient la méthode d'élément, mais aucune des méthodes Array.prototype, elles ne peuvent donc pas être utilisées sur les NodeLists.

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

Matt Lo
la source
8
Alors, puisqu'il s'agit d'une liste, pourquoi est-elle conçue de cette façon? Qu'est-ce qui les empêchait de faire de la chaîne myNodeList --> NodeList.prototype --> Array.prototype --> Object.prototype --> null:?
Igor Pantović
14

Si vous souhaitez utiliser forEach sur NodeList, copiez simplement cette fonction depuis Array:

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

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

document.querySelectorAll('td').forEach(function(o){
   o.innerHTML = 'text';
});
AlexTR
la source
4
Sauf que la mutation des classes de base n'est pas très explicite pour le lecteur moyen. En d'autres termes, au fond du code, vous devez vous souvenir de chaque personnalisation que vous apportez aux objets de base du navigateur. Se fier à la documentation MDN n'est plus utile car les objets ont changé de comportement par rapport à la norme. Il est préférable d'appliquer explicitement le prototype au moment de l'appel afin que le lecteur puisse facilement se rendre compte que forEach est une idée empruntée et non quelque chose qui fait partie de la définition du langage. Voir la réponse de @akuhn ci-dessus.
Sukima
@Sukima égaré par de mauvais locaux. Dans ce cas spécifique, une approche donnée permet à NodeList de se comporter comme prévu par le développeur. C'est le moyen le plus approprié de résoudre le problème de classe du système. (NodeList semble être inachevé et devrait être corrigé dans les futures versions du langage.)
Daniel Garmoshka
1
maintenant que NodeList.forEach existe, cela devient un polyfill d'une simplicité hilarante!
Damon
7

Dans ES2015, vous pouvez maintenant utiliser la forEachméthode pour le nodeList.

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

Voir le lien MDN

Cependant, si vous souhaitez utiliser des collections HTML ou d'autres objets de type tableau, dans es2015, vous pouvez utiliser Array.from()method. Cette méthode prend un objet de type tableau ou itérable (y compris nodeList, HTML Collections, strings, etc.) et retourne 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 Array.from()méthode est shimmable, vous pouvez l'utiliser dans le code es5 comme celui-ci

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

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

Pour vérifier la prise en charge actuelle du navigateur .

OU

une autre façon es2015 est d'utiliser l'opérateur de propagation.

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

Opérateur de diffusion MDN

Spread Operator - Prise en charge du navigateur

Mishel Tanvir Habib
la source
2

Ma solution:

//foreach for nodeList
NodeList.prototype.forEach = Array.prototype.forEach;
//foreach for HTML collection(getElementsByClassName etc.)
HTMLCollection.prototype.forEach = Array.prototype.forEach;
Bakos Bence
la source
1
Ce n'est souvent pas une bonne idée d'étendre les fonctionnalités de DOM à travers des prototypes, en particulier dans les anciennes versions d'IE ( article ).
KFE
0

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

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

randominstanceOfLivingThing
la source
Mais y a-t-il une raison particulière pour laquelle il ne devrait pas être mis en œuvre? JQuery et Dojo l'ont implémenté dans leurs propres bibliothèques
Snakes and Coffee
2
mais comment est-ce un conflit de conception?
Serpents et café
0

Vérifiez MDN pour NodeList.forEach spécification.

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

Dans IE, utilisez la réponse d'Akuhn :

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