forEach не является ошибкой функции с массивом JavaScript

145

Я пытаюсь сделать простой цикл:

const parent = this.el.parentElement
console.log(parent.children)
parent.children.forEach(child => {
  console.log(child)
})

Но я получаю следующую ошибку:

VM384: 53 Uncaught TypeError: parent.children.forEach не является функцией

Хотя parent.childrenжурналы:

введите описание изображения здесь

В чем может быть проблема?

Примечание: вот JSFiddle .

alexchenco
источник
Та же проблема возникает с element.siblings
Даут
@ Да, потому что element.siblings возвращает HTMLCollection, а HTMLCollections не имеют метода forEach ()
Фреддо,
1
эй ты, поисковик Google! если вы читаете эту двойную проверку, что это forEach с большой буквы E вместо foreach ....
Роберт Синклер

Ответы:

127

Первый вариант: вызвать forEach косвенно

Объект parent.childrenтипа Array. Используйте следующее решение:

const parent = this.el.parentElement;

Array.prototype.forEach.call(parent.children, child => {
  console.log(child)
});

Тип parent.childrenis NodeList, который является объектом типа Array, потому что:

  • Содержит lengthсвойство, которое указывает количество узлов
  • Каждый узел представляет собой значение свойства с числовым именем, начиная с 0: {0: NodeObject, 1: NodeObject, length: 2, ...}

Смотрите более подробную информацию в этой статье .


Второй вариант: использовать итеративный протокол

parent.childrenявляется HTMLCollection: который реализует итеративный протокол . В среде ES2015 вы можете использовать HTMLCollectionлюбую конструкцию, которая принимает итерации.

Используйте HTMLCollectionс оператором распространения:

const parent = this.el.parentElement;

[...parent.children].forEach(child => {
  console.log(child);
});

Или с for..ofциклом (который является моим предпочтительным вариантом):

const parent = this.el.parentElement;

for (const child of parent.children) {
  console.log(child);
}
Дмитрий Павлутин
источник
Когда я использую ваше решение, у меня больше нет проблем, но код внутри анонимной функции не выполняется. .so ..
Джереми
Какой браузер вы используете, чтобы parent.children сообщал вам, что это nodeList. На Firefox он говорит мне, что это HTMLCollection. Если бы это был список узлов, .forEach () работал бы
Фреддо
104

parent.childrenэто не массив. Это HTMLCollection и у него нет forEachметода. Вы можете сначала преобразовать его в массив. Например в ES6:

Array.from(parent.children).forEach(child => {
    console.log(child)
});

или используя оператор распространения:

[...parent.children].forEach(function (child) {
    console.log(child)
});
madox2
источник
9
Я предпочитаю это решение гораздо больше, чем возиться с прототипом Array.
Даут
И этот ответ является (одним из) правильных ответов на вопрос ОП. parent.children - это коллекция HTMLC, у которой нет метода
.forEach
18

parent.childrenвернет список списка узлов , технически HTML-коллекцию . Это объект типа массива, но не массив, поэтому вы не можете напрямую вызывать функции массива над ним. В этом контексте вы можете использовать, Array.from()чтобы преобразовать это в реальный массив,

Array.from(parent.children).forEach(child => {
  console.log(child)
})
Раджапрабху Аравиндасами
источник
Нет, parent.children не возвращает нодлист, а коллекцию HTML. Не то же самое. Если бы это был нодлист, .forEach работал бы
Фреддо
12

Более наивная версия, по крайней мере, вы уверены, что она будет работать на всех устройствах без конвертации и ES6:

const children = parent.children;
for (var i = 0; i < children.length; i++){
    console.log(children[i]);
}

https://jsfiddle.net/swb12kqn/5/

джинсовый
источник
2
Голосование вызвано тем, что все эти новые функции ES6 делают точно такую ​​же старую добрую вещь, которая была доступна, но в грязной манере, как всегда с JS
Фреддо
8

parent.childrenявляется HTMLCollectionобъектом, похожим на массив. Во- первых, вы должны преобразовать его в реальной Arrayдля использования Array.prototypeметодов.

const parent = this.el.parentElement
console.log(parent.children)
[].slice.call(parent.children).forEach(child => {
  console.log(child)
})
Дмитрий
источник
2
Или не конвертируйте его, а используйте .call () для .forEach ()?
nnnnnn
@nnnnnn Смотрите мой ответ ниже.
Дмитрий Павлутин
Есть много способов конвертировать массивоподобный объект в массив :) Это один из них
Дмитрий
@DmitriyLoskutov Вам не нужно конвертировать его - JavaScript - это язык утки. Просто используйте эту функцию.
Дмитрий Павлутин
5

Это потому, что parent.childrenэто NodeList , и он не поддерживает .forEachметод (так как NodeList представляет собой массив, похожий на структуру, но не массив), поэтому попробуйте вызвать его, сначала преобразовав его в массив, используя

var children = [].slice.call(parent.children);
children.forEach(yourFunc);
Аммар Хасан
источник
Нет, это не NodeList, это HTML коллекция
Фреддо
5

В этом нет необходимостиforEach , вы можете выполнять итерацию, используя только fromвторой параметр, например:

let nodeList = [{0: [{'a':1,'b':2},{'c':3}]},{1:[]}]
Array.from(nodeList, child => {
  console.log(child)
});

Armfoot
источник
Печальная новость заключается в том, что parent.children не является nodeList ... .from () не будет работать.
Фреддо
@Cedric, если ваш объект не является NodeList, вам следует задать новый вопрос специально для его решения. Здесь понижающее голосование используется, когда ответ по сути неверен или вреден, и, как видно из фрагмента кода, все элементы объекта повторяются и печатаются, что и было целью вопроса ОП.
Армфут
Да, проблема в том, что вопрос ОП касался коллекции HTML, а не нодлиста ... Так что ответ был просто не ответом на вопрос
Фреддо
@Cedric этот ответ также будет перебирать HTML-коллекцию, поскольку Array.fromпреобразует объект, указанный в первом параметре, в массив. Результат такой же, как в ответе madox2, без необходимости дополнительного forEachцикла ( Array.fromMDN документы).
Armfoot
4

Если вы пытаетесь перебрать NodeListподобное:

const allParagraphs = document.querySelectorAll("p");

Я настоятельно рекомендую сделать это следующим образом:

Array.prototype.forEach.call(allParagraphs , function(el) {
    // Write your code here
})

Лично я пробовал несколько способов, но большинство из них не работало, потому что я хотел перебрать NodeList, но этот работает как шарм, попробуй!

NodeListНе является массив, но мы рассматриваем его как массив, используя Array.Таким образом, вы должны знать , что она не поддерживается в старых браузерах!

Нужна дополнительная информация о NodeList? Пожалуйста, прочитайте его документацию по MDN .

Elharony
источник
1
Этот ответ, очевидно, работает на nodeList. Проблема в том, что parent.children возвращает HTML-коллекцию, которая не является nodeList ...
Фреддо
3

Поскольку вы используете функции ES6 ( функции стрелок ), вы также можете просто использовать цикл for следующим образом:

for(let child of [{0: [{'a':1,'b':2},{'c':3}]},{1:[]}]) {
  console.log(child)
}

Armfoot
источник
Upvoted. Какое искажение, синтаксис ES6, хотя ... Заставляет меня хотеть плакать, и я прихожу из C ++ фона ...
Фреддо
1

Вы можете проверить, правильно ли вы ввели forEach , если вы ввели foreach, как и в других языках программирования, это не сработает.

Абдельсалам Мегахед
источник
0

Вы можете использовать childNodesвместо children, childNodesтакже более надежно, учитывая проблемы совместимости браузера, больше информации здесь :

parent.childNodes.forEach(function (child) {
    console.log(child)
});

или используя оператор распространения:

[...parent.children].forEach(function (child) {
    console.log(child)
});
Сайед
источник