Для цикла для элементов HTMLCollection

Я пытаюсь установить получить идентификатор всех элементов в HTMLCollectionOf. Я написал следующий код:

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

Но я получил следующий вывод в консоли:

event1
undefined

это не то, что я ожидал. Почему второй вывод консоли undefined а первый вывод консоли - event1?

Ответ 1

Резюме (добавлено в декабре 2018 г.)

Никогда не используйте for/in для итерации nodeList или HTMLCollection. Причины, чтобы избежать этого, описаны ниже.

Все последние версии современных браузеров (Safari, Firefox, Chrome, Edge) все поддерживают for/of итераций в списках DOM, таких как nodeList или HTMLCollection.

Вот пример:

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

Чтобы включить старые браузеры (включая такие, как IE), это будет работать везде:

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

Пояснение, почему вы не должны использовать for/in

for/in предназначен для итерации свойств объекта. Это означает, что он вернет все итерируемые свойства объекта. Хотя может показаться, что он работает для массива (возвращая элементы массива или элементы псевдомассива), он также может возвращать другие свойства объекта, отличные от ожидаемых от элементов, подобных массиву. И, угадайте, что, HTMLCollection или объект nodeList могут иметь и другие свойства, которые будут возвращены с итерацией for/in. Я только что попробовал это в Chrome, и повторяя его так, как вы его повторяли, получит элементы в списке (индексы 0, 1, 2 и т.д.), Но также получит свойства length и item. Итерация for/in просто не будет работать для HTMLCollection.


Смотрите http://jsfiddle.net/jfriend00/FzZ2H/ почему вы не можете перебирать в HTMLCollection с for/in.

В Firefox ваша итерация for/in будет возвращать эти элементы (все итерируемые свойства объекта):

0
1
2
item
namedItem
@@iterator
length

Надеюсь, теперь вы можете понять, почему вы хотите использовать вместо for (var я = 0; я < list.length; i++) чтобы вы просто получили 0, 1 и 2 в своей итерации.


Ниже приводится эволюция эволюции браузеров за период 2015-2018 гг., Которая дает вам дополнительные способы итерации. В современных браузерах ничего из этого не требуется, поскольку вы можете использовать опции, описанные выше.

Обновление для ES6 в 2015 году

В ES6 добавлен Array.from Array.from(), который преобразует Array.from() структуру в реальный массив. Это позволяет прямо перечислять список следующим образом:

"use strict";

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

Рабочая демонстрация (в Firefox, Chrome и Edge на апрель 2016 г.): https://jsfiddle.net/jfriend00/8ar4xn2s/


Обновление для ES6 в 2016 году

Теперь вы можете использовать ES6 for/of construct с NodeList и HTMLCollection, просто добавив это в свой код:

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

Затем вы можете сделать:

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

Это работает в текущей версии Chrome, Firefox и Edge. Это работает, потому что он присоединяет итератор Array к прототипам NodeList и HTMLCollection, так что, когда for/of итерирует их, он использует итератор Array для их итерации.

Рабочая демонстрация: http://jsfiddle.net/jfriend00/joy06u4e/.


Второе обновление для ES6 в декабре 2016 года

По состоянию на декабрь 2016 года поддержка Symbol.iterator была встроена в Chrome v54 и Firefox v50, поэтому приведенный ниже код работает сам по себе. Он еще не встроен в Edge.

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

Рабочая демонстрация (в Chrome и Firefox): http://jsfiddle.net/jfriend00/3ddpz8sp/

Третье обновление для ES6 в декабре 2017 года

По состоянию на декабрь 2017 г. эта возможность работает в Edge 41.16299.15.0 для nodeList как в document.querySelectorAll(), но не для HTMLCollection как в document.getElementsByClassName() поэтому необходимо вручную назначить итератор, чтобы использовать его в Edge для HTMLCollection. Это полная загадка, почему они исправляют один тип коллекции, а не другой. Но вы можете по крайней мере использовать результат document.querySelectorAll() с ES6 for/of синтаксиса в текущих версиях Edge сейчас.

Я также обновил вышеупомянутый jsFiddle, чтобы он тестировал HTMLCollection и nodeList отдельности и записывал выходные данные в самом jsFiddle.

Четвертое обновление для ES6 в марте 2018 года

Для mesqueeeb, поддержка Symbol.iterator также встроена в Safari, так что вы можете использовать for (let item of list) для document.getElementsByClassName() или document.querySelectorAll().

Пятое обновление для ES6 в апреле 2018 года

Очевидно, поддержка итерации HTMLCollection с for/of появится в Edge 18 осенью 2018 года.

Шестое обновление для ES6 в ноябре 2018 года

Я могу подтвердить, что с Microsoft Edge v18 (который включен в Windows Update 2018 года) теперь вы можете выполнять итерацию HTMLCollection и NodeList с for/of в Edge.

Итак, теперь все современные браузеры содержат встроенную поддержку for/of итераций объектов HTMLCollection и NodeList.

Ответ 2

Вы не можете использовать for/in в NodeList или HTMLCollection. Однако вы можете использовать некоторые методы Array.prototype, если вы их используете .call() и передаете в NodeList или HTMLCollection как this.

Поэтому рассмотрим следующее в качестве альтернативы jfriend00 for цикла:

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

Там хорошая статья о MDN, которая охватывает эту технику. Обратите внимание на их предупреждение о совместимости с браузером:

[...] передача объекта-хозяина (например, NodeList), поскольку this для собственного метода (например, forEach), не гарантируется работать во всех браузерах и, как известно, в некоторых случаях не работает.

Таким образом, хотя этот подход удобен, цикл for может быть самым совместимым с браузером решением.

Обновление (30 августа 2014 г.): В конце концов вы сможете использовать ES6 for/of !

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

Он уже поддерживается в последних версиях Chrome и Firefox.

Ответ 3

В ES6 вы можете сделать что-то вроде [...collection] или Array.from(collection),

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

Например:-

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

Ответ 4

вы можете добавить две строки:

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

HTMLCollection - это return by getElementsByClassName и getElementsByTagName

NodeList возвращает запрос querySelectorAll

Подобно этому вы можете сделать forEach:

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

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

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

Ответ 5

У меня возникла проблема с использованием forEach в IE 11, а также Firefox 49

Я нашел обходное решение, подобное этому

Array.prototype.slice.call(document.getElementsByClassName("events")).forEach(function (key) {
        console.log(key.id);
    }

Ответ 6

По состоянию на март 2016 года в Chrome 49.0 for...of работает для HTMLCollection:

this.headers = this.getElementsByTagName("header");

for (var header of this.headers) {
    console.log(header); 
}

Смотрите здесь документацию.

Но он работает, только если вы применили следующее обходное решение до с помощью for...of:

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

То же самое необходимо для использования for...of с NodeList:

NamedNodeMap.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

Я верю/надеюсь, что for...of скоро будет работать без вышеупомянутого обходного пути. Открытый вопрос здесь:

https://bugs.chromium.org/p/chromium/issues/detail?id=401699

Обновление: См. комментарий Expenzor ниже: это было исправлено с апреля 2016 года. Вам не нужно добавлять HTMLCollection.prototype [Symbol.iterator] = Array.prototype [Symbol.iterator]; для итерации по HTMLCollection с для... из

Ответ 7

Альтернатива Array.from заключается в использовании Array.prototype.forEach.call

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

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

ЭСТ...

Ответ 8

Нет никаких оснований использовать функции es6 для выхода for цикла, если вы находитесь на IE9 или выше.

В ES5 есть два хороших варианта. Во-первых, вы можете "заимствовать" Array forEach как упоминается.

Но еще лучше...

Используйте Object.keys(), который имеет forEach

Object.keys по сути эквивалентен выполнению for... in с HasOwnProperty, но является более плавным.

var eventNodes = document.getElementsByClassName("events");
Object.keys(eventNodes).forEach(function (key) {
    console.log(eventNodes[key].id);
});

Ответ 9

На Edge

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);
    }
  }
}

Ответ 10

Вы хотите изменить его на

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
}

Ответ 11

если вы используете более старые версии ES (например, ES5), вы можете использовать as any:

for (let element of elementsToIterate as any) {
      console.log(element);
}

Ответ 12

Простой обходной путь, который я всегда использую

let list = document.getElementsByClassName("events");
let listArr = Array.from(list)

После этого вы можете запустить любые нужные методы Array на выбор

listArr.map(item => console.log(item.id))
listArr.forEach(item => console.log(item.id))
listArr.reverse()