J'essaie de définir get id de tous les éléments dans une HTMLCollectionOf
. J'ai écrit le code suivant:
var list = document.getElementsByClassName("events");
console.log(list[0].id);
for (key in list) {
console.log(key.id);
}
Mais j'ai la sortie suivante dans la console:
event1
undefined
ce qui n'est pas ce à quoi je m'attendais. Pourquoi la deuxième sortie de console est-elle undefined
alors que la première sortie de console est event1
?
Résumé (ajouté en décembre 2018)
N'utilisez jamais for/in
pour itérer une liste de noeuds ou une collection HTMLC. Les raisons pour l'éviter sont décrites ci-dessous.
Toutes les versions récentes des navigateurs modernes (Safari, Firefox, Chrome, Edge) prennent en charge l'itération for/of
sur des listes DOM telles que nodeList
ou HTMLCollection
.
Voici un exemple:
var list = document.getElementsByClassName("events");
for (let item of list) {
console.log(item.id);
}
Pour inclure les anciens navigateurs (y compris des choses comme IE), cela fonctionnera partout:
var list= document.getElementsByClassName("events");
for (var i = 0; i < list.length; i++) {
console.log(list[i].id); //second console output
}
Explication de la raison pour laquelle vous ne devriez pas utiliser for/in
for/in
est destiné à itérer les propriétés d'un objet. Cela signifie qu'il renverra toutes les propriétés itérables d'un objet. Bien que cela puisse sembler fonctionner pour un tableau (renvoyer des éléments de tableau ou des éléments de pseudo-tableaux), il peut également renvoyer d'autres propriétés de l'objet qui ne correspondent pas à ce que vous attendez des éléments de type tableau. Et, devinez quoi, un objet HTMLCollection
ou nodeList
peut avoir d'autres propriétés qui seront retournées avec une itération for/in
. Je viens d’essayer cela dans Chrome et en le répétant, vous récupérerez les éléments de la liste (index 0, 1, 2, etc.), mais également les propriétés length
et item
. L'itération for/in
ne fonctionnera tout simplement pas pour une collection HTMLCollection.
Voir http://jsfiddle.net/jfriend00/FzZ2H/ pour savoir pourquoi vous ne pouvez pas itérer une collection HTMLC avec for/in
.
Dans Firefox, votre itération for/in
renverrait ces éléments (toutes les propriétés itérables de l'objet):
0
1
2
item
namedItem
@@iterator
length
Si tout va bien, vous pouvez maintenant voir pourquoi vous voulez utiliser for (var i = 0; i < list.length; i++)
à la place, de sorte que vous n'obtenez que 0
, 1
et 2
dans votre itération.
Vous trouverez ci-dessous une évolution de la manière dont les navigateurs ont évolué au cours de la période 2015-2018, en vous offrant des moyens supplémentaires d’itérer. Aucun de ceux-ci n'est maintenant nécessaire dans les navigateurs modernes, car vous pouvez utiliser les options décrites ci-dessus.
Mise à jour pour ES6 en 2015
Array.from()
a été ajouté à ES6 pour convertir une structure semblable à un tableau à un tableau réel. Cela permet d’énumérer directement une liste comme ceci:
"use strict";
Array.from(document.getElementsByClassName("events")).forEach(function(item) {
console.log(item.id);
});
Démonstration de travail (dans Firefox, Chrome et Edge à compter d'avril 2016): https://jsfiddle.net/jfriend00/8ar4xn2s/
Mise à jour de l'ES6 en 2016
Vous pouvez maintenant utiliser la construction ES6 pour/de avec une NodeList
et une HTMLCollection
en ajoutant simplement ceci à votre code:
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
Ensuite, vous pouvez faire:
var list = document.getElementsByClassName("events");
for (var item of list) {
console.log(item.id);
}
Cela fonctionne dans la version actuelle de Chrome, Firefox et Edge. Cela fonctionne car il lie l'itérateur Array aux prototypes NodeList et HTMLCollection de sorte que, lorsque pour/des itère, il utilise l'itérateur Array pour les itérer.
Démo de travail: http://jsfiddle.net/jfriend00/joy06u4e/ .
Deuxième mise à jour pour ES6 en décembre 2016
Depuis décembre 2016, la prise en charge de Symbol.iterator
a été intégrée à Chrome v54 et à Firefox v50, de sorte que le code ci-dessous fonctionne de manière autonome. Il n'est pas encore intégré à Edge.
var list = document.getElementsByClassName("events");
for (let item of list) {
console.log(item.id);
}
Démo de travail (sous Chrome et Firefox): http://jsfiddle.net/jfriend00/3ddpz8sp/
Troisième mise à jour pour ES6 en décembre 2017
Depuis décembre 2017, cette fonctionnalité fonctionne dans Edge 41.16299.15.0 pour un nodeList
dans document.querySelectorAll()
, mais pas un HTMLCollection
dans document.getElementsByClassName()
. Vous devez donc affecter manuellement l'itérateur à l'utiliser dans Edge pour un HTMLCollection
. La raison pour laquelle ils ont résolu un type de collection, mais pas l’autre, est un mystère total. Toutefois, vous pouvez au moins utiliser le résultat de document.querySelectorAll()
avec la syntaxe ES6 for/of
dans les versions actuelles d'Edge maintenant.
J'ai également mis à jour le fichier jsFiddle ci-dessus afin qu'il teste HTMLCollection
et nodeList
séparément et capture la sortie dans le fichier jsFiddle lui-même.
Quatrième mise à jour pour ES6 en mars 2018
Comme indiqué précédemment, le support Symbol.iterator
a également été intégré à Safari, vous pouvez donc utiliser for (let item of list)
pour document.getElementsByClassName()
ou document.querySelectorAll()
.
Cinquième mise à jour pour ES6 en avril 2018
Apparemment, le support pour l'itération d'une HTMLCollection
avec for/of
arrivera dans Edge 18 à l'automne 2018.
Sixième mise à jour pour ES6 en novembre 2018
Je peux confirmer qu'avec Microsoft Edge v18 (inclus dans la mise à jour de l'automne 2018 de Windows), vous pouvez désormais effectuer une itération à la fois sur une collection HTMLCollection et sur une liste de noeuds avec pour/de dans Edge.
Ainsi, tous les navigateurs modernes contiennent désormais une prise en charge native de l'itération for/of
des objets HTMLCollection et NodeList.
Vous ne pouvez pas utiliser for
in
sur NodeList
s ou HTMLCollection
s. Cependant, vous pouvez utiliser certaines méthodes Array.prototype
, tant que vous les .call()
et transmettez NodeList
ou HTMLCollection
comme this
.
Donc, considérez ce qui suit comme une alternative à la boucle for
de jfriend00 :
var list= document.getElementsByClassName("events");
[].forEach.call(list, function(el) {
console.log(el.id);
});
Il existe un bon article sur MDN qui couvre cette technique. Notez leur avertissement concernant la compatibilité du navigateur:
[...] en passant un objet hôte (comme une
NodeList
) en tant quethis
à une méthode native (telle queforEach
) n’est pas garantie de fonctionner dans tous les navigateurs et est connu pour échouer dans certains.
Ainsi, bien que cette approche soit pratique, une boucle for
peut être la solution la plus compatible avec les navigateurs.
_/Mise à jour (30 août 2014): Vous pourrez éventuellement utiliser ES6 for
/of
!
var list = document.getElementsByClassName("events");
for (el of list)
console.log(el.id);
Il est déjà pris en charge dans les versions récentes de Chrome et Firefox.
Dans ES6, vous pouvez faire quelque chose comme [...collection]
ou Array.from(collection)
,
let someCollection = document.querySelectorAll(someSelector)
[...someCollection].forEach(someFn)
//or
Array.from(collection).forEach(someFn)
vous pouvez ajouter ces deux lignes:
HTMLCollection.prototype.forEach = Array.prototype.forEach;
NodeList.prototype.forEach = Array.prototype.forEach;
HTMLCollection est renvoyé par getElementsByClassName et getElementsByTagName
NodeList est renvoyé par querySelectorAll
Comme cela, vous pouvez faire un forEach:
var selections = document.getElementsByClassName('myClass');
/* alternative :
var selections = document.querySelectorAll('.myClass');
*/
selections.forEach(function(element, i){
//do your stuffs
});
J'ai eu un problème d'utilisation de forEach dans IE 11 et aussi Firefox 49
J'ai trouvé une solution de contournement comme celle-ci
Array.prototype.slice.call(document.getElementsByClassName("events")).forEach(function (key) {
console.log(key.id);
}
Depuis mars 2016, dans Chrome 49.0, for...of
fonctionne pour HTMLCollection
:
this.headers = this.getElementsByTagName("header");
for (var header of this.headers) {
console.log(header);
}
Voir ici la documentation .
Mais cela ne fonctionne que si vous appliquez la solution suivante before à l'aide du for...of
:
HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
La même chose est nécessaire pour utiliser for...of
avec NodeList
:
NamedNodeMap.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
Je crois/espère que for...of
fonctionnera bientôt sans la solution de contournement ci-dessus. La question ouverte est ici:
https://bugs.chromium.org/p/chromium/issues/detail?id=401699
Mise à jour: Voir le commentaire de Expenzor ci-dessous: Ce problème a été résolu en avril 2016. Il n'est pas nécessaire d'ajouter HTMLCollection.prototype [Symbol.iterator] = Array.prototype [Symbol.iterator]. sur une collection HTMLC avec pour ... de
Alternative à Array.from
est d'utiliser Array.prototype.forEach.call
pour chaque: Array.prototype.forEach.call(htmlCollection, i => { console.log(i) });
carte: Array.prototype.map.call(htmlCollection, i => { console.log(i) });
ect ...
Il n'y a aucune raison d'utiliser les fonctionnalités es6 pour échapper à la boucle for
si vous utilisez IE9 ou une version ultérieure.
Dans ES5, il existe deux bonnes options. Tout d’abord, vous pouvez «emprunter» le nom Array
de forEach
comme evan mentionne .
Mais encore mieux ...
Object.keys()
, qui do a forEach
Object.keys
est essentiellement équivalent à faire un for... in
avec un HasOwnProperty
, mais est plus lisse.
var eventNodes = document.getElementsByClassName("events");
Object.keys(eventNodes).forEach(function (key) {
console.log(eventNodes[key].id);
});
Sur le bord
if(!NodeList.prototype.forEach) {
NodeList.prototype.forEach = function(fn, scope) {
for(var i = 0, len = this.length; i < len; ++i) {
fn.call(scope, this[i], i, this);
}
}
}
Vous voulez le changer en
var list= document.getElementsByClassName("events");
console.log(list[0].id); //first console output
for (key in list){
console.log(list[key].id); //second console output
}
Contournement facile que j'utilise toujours
let list = document.getElementsByClassName("events");
let listArr = Array.from(list)
Après cela, vous pouvez exécuter n’importe quelle méthode Array de votre choix.
listArr.map(item => console.log(item.id))
listArr.forEach(item => console.log(item.id))
listArr.reverse()
si vous utilisez des versions plus anciennes d'ES (ES5 par exemple), vous pouvez utiliser as any
:
for (let element of elementsToIterate as any) {
console.log(element);
}