675 Stimmen

Schleife für HTMLCollection-Elemente

Ich versuche, die ID aller Elemente in einem HTMLCollectionOf zu erhalten. Ich habe den folgenden Code geschrieben:

var list = document.getElementsByClassName("events");
console.log(list[0].id);
for (key in list) {
    console.log(key.id);
}

Aber ich habe die folgende Ausgabe in der Konsole erhalten:

event1
undefined

was nicht das ist, was ich erwartet hatte. Warum ist die zweite Konsolenausgabe undefined, während die erste Konsolenausgabe event1 ist?

1345voto

jfriend00 Punkte 632952

Als Antwort auf die ursprüngliche Frage verwenden Sie for/in falsch. In Ihrem Code ist key der Index. Um also den Wert aus dem Pseudo-Array zu erhalten, müssten Sie list[key] und um die ID zu erhalten, würden Sie list[key].id verwenden. Aber Sie sollten das sowieso nicht mit for/in machen.

Zusammenfassung (hinzugefügt im Dez. 2018)

Verwenden Sie niemals for/in, um eine nodeList oder eine HTMLCollection zu durchlaufen. Die Gründe, dies zu vermeiden, werden unten beschrieben.

Alle aktuellen Versionen der modernen Browser (Safari, Firefox, Chrome, Edge) unterstützen alle die Iteration mit for/of auf DOM-Listen wie z.B. nodeList oder HTMLCollection.

Hier ist ein Beispiel:

var list = document.getElementsByClassName("events");
for (let item of list) {
    console.log(item.id);
}

Um auch ältere Browser (einschließlich Dinge wie IE) einzuschließen, funktioniert dies überall:

var list = document.getElementsByClassName("events");
for (var i = 0; i < list.length; i++) {
    console.log(list[i].id); //zweite Konsolenausgabe
}

Erklärung, warum Sie for/in nicht verwenden sollten:

for/in dient dazu, die Eigenschaften eines Objekts zu durchlaufen. Das bedeutet, dass es alle durchlässigen Eigenschaften eines Objekts zurückgeben wird. Auch wenn es für ein Array zu funktionieren scheint (die Array-Elemente oder Pseudo-Array-Elemente zurückgibt), kann es auch andere Eigenschaften des Objekts zurückgeben, die nicht die erwarteten Array-ähnlichen Elemente sind. Und, raten Sie mal, ein HTMLCollection- oder nodeList-Objekt kann auch andere Eigenschaften haben, die bei einer for/in-Iteration zurückgegeben werden. Ich habe es gerade in Chrome ausprobiert und wenn Sie es so durchlaufen wie Sie es getan haben, werden die Elemente in der Liste (Indexe 0, 1, 2 etc.) zurückgegeben, aber es werden auch die Eigenschaften length und item zurückgegeben. Die for/in-Iteration funktioniert einfach nicht für eine HTMLCollection.


Siehe http://jsfiddle.net/jfriend00/FzZ2H/ warum Sie eine HTMLCollection nicht mit for/in durchlaufen können.

In Firefox würde Ihre for/in-Iteration diese Elemente zurückgeben (alle durchlässigen Eigenschaften des Objekts):

0
1
2
item
namedItem
@@iterator
length

Hoffentlich sehen Sie nun, warum Sie stattdessen for (var i = 0; i < list.length; i++) verwenden möchten, um nur 0, 1 und 2


Entwicklung der Browserunterstützung für die Iteration von NodeList und HTMLCollection

Nachfolgend finden Sie eine Evolution, wie sich die Browser im Zeitraum 2015-2018 entwickelt haben und Ihnen zusätzliche Möglichkeiten zur Iteration bieten. Keine dieser Möglichkeiten wird in modernen Browsern benötigt, da Sie die oben beschriebenen Optionen verwenden können.

Update für ES6 im Jahr 2015

In ES6 wurde Array.from() hinzugefügt, das eine array-ähnliche Struktur in ein tatsächliches Array umwandelt. Das ermöglicht es, eine Liste direkt wie folgt aufzuzählen:

"use strict";

Array.from(document.getElementsByClassName("events")).forEach(function(item) {
   console.log(item.id);
});

Funktionsdemo (in Firefox, Chrome und Edge per April 2016): https://jsfiddle.net/jfriend00/8ar4xn2s/


Update für ES6 im Jahr 2016

Sie können nun den ES6 for/of-Konstrukt mit einem NodeList und einer HTMLCollection verwenden, indem Sie einfach dies zu Ihrem Code hinzufügen:

NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

Dann können Sie folgendes tun:

var list = document.getElementsByClassName("events");
for (var item of list) {
    console.log(item.id);
}

Dies funktioniert in der aktuellen Version von Chrome, Firefox und Edge. Dies funktioniert, weil es den Array-Iterator an die Prototypen von NodeList und HTMLCollection anhängt, so dass er den Array-Iterator verwendet, um sie zu durchlaufen, wenn for/of sie durchläuft.

Funktionsdemo: http://jsfiddle.net/jfriend00/joy06u4e/.


Zweites Update für ES6 im Dezember 2016

Seit Dezember 2016 ist die Unterstützung für Symbol.iterator in Chrome v54 und Firefox v50 integriert, so dass der folgende Code von selbst funktioniert. Es ist noch nicht in Edge eingebaut.

var list = document.getElementsByClassName("events");
for (let item of list) {
    console.log(item.id);
}

Funktionsdemo (in Chrome und Firefox): http://jsfiddle.net/jfriend00/3ddpz8sp/

Drittes Update für ES6 im Dezember 2017

Seit Dezember 2017 funktioniert diese Möglichkeit in Edge 41.16299.15.0 für eine nodeList wie in document.querySelectorAll(), nicht jedoch für eine HTMLCollection wie in document.getElementsByClassName(), sodass Sie den Iterator manuell zuweisen müssen, um ihn in Edge für eine HTMLCollection zu verwenden. Es ist ein komplettes Rätsel, warum sie einen Sammlungstyp reparieren würden, aber nicht den anderen. Aber Sie können zumindest das Ergebnis von document.querySelectorAll() mit der ES6 for/of-Syntax jetzt in aktuellen Versionen von Edge verwenden.

Ich habe auch das oben genannte jsFiddle aktualisiert, damit es sowohl HTMLCollection als auch nodeList getrennt testet und die Ausgabe im jsFiddle selbst erfasst.

Viertes Update für ES6 im März 2018

Laut mesqueeeb ist die Unterstützung für Symbol.iterator jetzt auch in Safari integriert, so dass Sie for (let item of list) für sowohl document.getElementsByClassName() als auch document.querySelectorAll() verwenden können.

Fünftes Update für ES6 im April 2018

Anscheinend wird die Unterstützung für die Iteration einer HTMLCollection mit for/of in Edge 18 im Herbst 2018 kommen.

Sechstes Update für ES6 im November 2018

Ich kann bestätigen, dass mit Microsoft Edge v18 (der im Windows Update im Herbst 2018 enthalten ist) können Sie jetzt sowohl eine HTMLCollection als auch eine NodeList mit for/of in Edge durchlaufen.

Also enthalten jetzt alle modernen Browser native Unterstützung für die Iteration von for/of sowohl bei den HTMLCollection- als auch NodeList-Objekten.

95voto

evanrmurphy Punkte 1786

Du kannst for/in nicht auf NodeLists oder HTMLCollections verwenden. Du kannst jedoch einige Methoden von Array.prototype verwenden, solange du sie mit .call() aufrufst und den NodeList oder HTMLCollection als this übergibst.

Betrachte also Folgendes als Alternative zur for-Schleife von jfriend00:

var list= document.getElementsByClassName("events");
[].forEach.call(list, function(el) {
    console.log(el.id);
});

Es gibt einen guten Artikel auf MDN, der diese Technik behandelt. Beachte jedoch ihre Warnung bezüglich der Browserkompatibilität:

[...] das Übergeben eines Host-Objekts (wie einem NodeList) als this an eine native Methode (wie forEach) ist nicht garantiert in allen Browsern zu funktionieren und scheitert in einigen bekanntermaßen.

Obwohl dieser Ansatz praktisch ist, könnte eine for-Schleife die browserkompatibelste Lösung sein.

Update (30. Aug 2014): Schließlich wird es möglich sein, ES6 for/of zu verwenden!

var list = document.getElementsByClassName("events");
for (const el of list)
  console.log(el.id);

Es wird bereits in aktuellen Versionen von Chrome und Firefox unterstützt.

87voto

mido Punkte 22624

In ES6, you could do something like [...collection], or Array.from(collection),

let someCollection = document.querySelectorAll(someSelector)
[...someCollection].forEach(someFn) 
//or
Array.from(collection).forEach(someFn)

Eg:-

    navDoms = document.getElementsByClassName('nav-container');
    Array.from(navDoms).forEach(function(navDom){
     //implement function operations
    });

translated to

In ES6 könnten Sie etwas wie [...collection] oder Array.from(collection) tun,

let someCollection = document.querySelectorAll(someSelector)
[...someCollection].forEach(someFn) 
//oder
Array.from(collection).forEach(someFn)

Zum Beispiel:-

    navDoms = document.getElementsByClassName('nav-container');
    Array.from(navDoms).forEach(function(navDom){
     //Funktionsoperationen implementieren
    });

17voto

mmnl Punkte 171

Du kannst diese zwei Zeilen hinzufügen:

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

HTMLCollection wird von getElementsByClassName und getElementsByTagName zurückgegeben

NodeList wird von querySelectorAll zurückgegeben

So kannst du ein forEach machen:

var selections = document.getElementsByClassName('myClass');

/* alternative :
var selections = document.querySelectorAll('.myClass');
*/

selections.forEach(function(element, i){
//do your stuffs
});

13voto

holmberd Punkte 1885

Alternative zu Array.from ist es, Array.prototype.forEach.call zu verwenden

forEach: Array.prototype.forEach.call(htmlCollection, i => { console.log(i) });

map: Array.prototype.map.call(htmlCollection, i => { console.log(i) });

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X