JS: повторение результата getElementsByClassName с использованием массива Array.forEach

Я хочу перебрать некоторые элементы DOM, я делаю это:

document.getElementsByClassName( "myclass" ).forEach( function(element, index, array) {
  //do stuff
});

но я получаю сообщение об ошибке: document.getElementsByClassName( "myclass" ). forEach не является функцией

Я использую Firefox 3, поэтому я знаю, что присутствуют как getElementsByClassName, так и Array.forEach. Это отлично работает:

[2, 5, 9].forEach( function(element, index, array) {
  //do stuff
});

Является результатом getElementsByClassName массива? Если нет, что это такое?

Ответ 1

Нет. Как указано в DOM4, это HTMLCollection (по крайней мере, в современных браузерах. Старые браузеры возвращали NodeList).

Во всех современных браузерах (почти во всех других IE <= 8) вы можете вызывать метод Array forEach, передавая ему список элементов (будь то HTMLCollection или NodeList) в качестве значения this:

var els = document.getElementsByClassName("myclass");

Array.prototype.forEach.call(els, function(el) {
    // Do stuff here
    console.log(el.tagName);
});

// Or
[].forEach.call(els, function (el) {...});

Если вы счастливы использовать ES6 (то есть вы можете спокойно игнорировать Internet Explorer или использовать ES5-транспортер), вы можете использовать Array.from:

Array.from(els).forEach((el) => {
    // Do stuff here
    console.log(el.tagName);
});

Ответ 2

Вы можете использовать Array.from для преобразования коллекции в массив, который намного чище, чем Array.prototype.forEach.call:

Array.from(document.getElementsByClassName("myclass")).forEach(
    function(element, index, array) {
        // do stuff
    }
);

В старых браузерах, которые не поддерживают Array.from, вам нужно использовать что-то вроде Babel.


ES6 также добавляет этот синтаксис:

[...document.getElementsByClassName("myclass")].forEach(
    (element, index, array) => {
        // do stuff
    }
);

Деструктурирование rest с помощью ... работает со всеми объектами, похожими на массивы, а не только с самими массивами, тогда для создания массива из значений используется старый добрый синтаксис массива.


Хотя альтернативная функция querySelectorAll (которая делает getElementsByClassName устаревшей) возвращает коллекцию, которая изначально имеет forEach, другие методы, такие как map или filter, отсутствуют, поэтому этот синтаксис все еще полезен:

[...document.querySelectorAll(".myclass")].map(
    (element, index, array) => {
        // do stuff
    }
);

[...document.querySelectorAll(".myclass")].map(element => element.innerHTML);

Ответ 4

Изменить: хотя тип возвращаемого текста был изменен в новых версиях HTML (см. обновленный ответ Tim Down), код ниже все еще работает.

Как говорили другие, это NodeList. Вот полный рабочий пример, который вы можете попробовать:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <script>
            function findTheOddOnes()
            {
                var theOddOnes = document.getElementsByClassName("odd");
                for(var i=0; i<theOddOnes.length; i++)
                {
                    alert(theOddOnes[i].innerHTML);
                }
            }
        </script>
    </head>
    <body>
        <h1>getElementsByClassName Test</h1>
        <p class="odd">This is an odd para.</p>
        <p>This is an even para.</p>
        <p class="odd">This one is also odd.</p>
        <p>This one is not odd.</p>
        <form>
            <input type="button" value="Find the odd ones..." onclick="findTheOddOnes()">
        </form>
    </body>
</html>

Это работает в IE 9, FF 5, Safari 5 и Chrome 12 на Win 7.

Ответ 5

Результатом getElementsByClassName() является не массив, а объект типа массива. В частности, он называется HTMLCollection, его не следует путать с NodeList (который имеет собственный метод forEach()).

Один простой способ с ES2015 преобразовать подобный массиву объект для использования с Array.prototype.forEach() который еще не был упомянут, состоит в том, чтобы использовать оператор распространения или синтаксис распространения:

const elementsArray = document.getElementsByClassName('myclass');

[...elementsArray].forEach((element, index, array) => {
    // do something
});

Ответ 6

Является ли результат getElementsByClassName массивом?

Нет

Если нет, что это такое?

Как и все методы DOM, которые возвращают несколько элементов, это NodeList, см. https://developer.mozilla.org/en/DOM/document.getElementsByClassName

Ответ 7

Как уже говорилось, getElementsByClassName возвращает HTMLCollection, который определяется как

[Exposed=Window]
interface HTMLCollection {
  readonly attribute unsigned long length;
  getter Element? item(unsigned long index);
  getter Element? namedItem(DOMString name);
};

Раньше некоторые браузеры вместо NodeList возвращали некоторые браузеры.

[Exposed=Window]
interface NodeList {
  getter Node? item(unsigned long index);
  readonly attribute unsigned long length;
  iterable<Node>;
};

Разница важна, поскольку DOM4 теперь определяет NodeLists как iterable.

В соответствии с Web IDL,

Объекты, реализующие интерфейс, объявленный как итерируемый поддержка повторяется для получения последовательности значений.

Примечание. В языковой привязке ECMAScript используется интерфейс, который   iterable будет иметь "записи", "forEach", "keys", "values" и   @@iterator свойства на объекте прототипа интерфейса.

Это означает, что если вы хотите использовать forEach, вы можете использовать метод DOM, который возвращает NodeList, например querySelectorAll.

document.querySelectorAll(".myclass").forEach(function(element, index, array) {
  // do stuff
});

Обратите внимание, что это пока еще не поддерживается. Также см. для каждого метода Node.childNodes?

Ответ 8

Он не возвращает Array, он возвращает NodeList.

Ответ 9

Это более безопасный способ:

var elements = document.getElementsByClassName("myclass");
for (var i = 0; i < elements.length; i++) myFunction(elements[i]);

Ответ 10

getElementsByClassName возвращает HTMLCollection в современных браузерах.

который является массивоподобным объектом, похожим на аргументы, который можно перебрать с помощью цикла for...of смотрите ниже, что MDN doc говорит об этом:

Оператор for... of создает цикл, повторяющийся над повторяемыми объектами, включая: встроенные объекты String, Array, Array-like (например, arguments или NodeList), TypedArray, Map, Set и определяемые пользователем итерируемые элементы. Он вызывает пользовательский итерационный хук с инструкциями, которые должны быть выполнены для значения каждого отдельного свойства объекта.

пример

for (let element of getElementsByClassName("classname")){
   element.style.display="none";
}

Ответ 11

Вот тест, который я создал на jsperf: https://jsperf.com/vanillajs-loop-through-elements-of-class

Самая популярная версия в Chrome и Firefox - это старый добрый цикл for в сочетании с document.getElementsByClassName:

var elements = document.getElementsByClassName('testClass'), elLength = elements.length;
for (var i = 0; i < elLength; i++) {
    elements.item(i).textContent = 'Tested';
};

В Safari этот вариант является победителем:

var elements = document.querySelectorAll('.testClass');
elements.forEach((element) => {
    element.textContent = 'Tested';
});

Если вам нужен наиболее удобный вариант для всех браузеров, он может быть следующим:

var elements = document.getElementsByClassName('testClass');
Array.from(elements).map(
    (element) => {
        return element.textContent = 'Tested';
    }
);