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 .


Така ж проблема виникає з element.siblings
Daut

@Daut та тому , що element.siblings повертає HTMLCollection і HTMLCollections не метод ForEach ()
Freddo

1
привіт, пошукач Google! якщо ви читаєте цю подвійну перевірку, що це для кожного з великої літери замість передбачення ....
Роберт Сінклер

Відповіді:


127

Перший варіант: викликати forEach побічно

parent.childrenЄ масив як об'єкт. Використовуйте таке рішення:

const parent = this.el.parentElement;

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

parent.childrenЄ NodeListтип, який є масивом як об'єкт , так як :

  • Він містить 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 ..
Jérémy

Який веб-переглядач ви використовуєте, щоб parent.children повідомив вам, що це nodeList. На Firefox він говорить, що це HTMLCollection. Якби це було нодліст, .forEach () буде працювати
Freddo

104

parent.childrenне є масивом. Це HTMLCollection, і він не має forEachметоду. Ви можете спочатку перетворити його в масив. Наприклад в ES6:

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

або за допомогою оператора спред:

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

9
Я вважаю за краще це рішення набагато більше, ніж возитися з прототипом Array.
Даут

І ця відповідь (одна з) правильних відповідей на питання ОП. parent.children є HTMLCollection , які не мають метод .forEach
Freddo

18

parent.childrenповерне список списку вузлів , технічно - колекція html . Це об’єкт, схожий на масив, але не масив, тому ви не можете безпосередньо викликати функції масиву над ним. У цьому контексті ви можете Array.from()перетворити це в реальний масив,

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

Ні, parent.children не повертає nodeList, а колекцію HTML. Не те саме. Якби це був Nodelist, .forEach буде працювати
Freddo

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
Freddo

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 Collection
Freddo

5

Немає потреби вforEach , ви можете повторити, використовуючи лише fromдругий параметр, наприклад:

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


Сумна новина полягає в тому, що parent.children - це не nodeList ... .from () не працюватиме.
Freddo

@Cedric, якщо ваш об'єкт не є NodeList, вам слід задати нове запитання, спеціально для його вирішення. Тут наведене покликання застосовується тоді, коли відповідь по суті невірний або шкідливий, і, як ви бачите за фрагментом коду, всі елементи об’єкта повторюються та друкуються, що було метою запитання ОП.
Armfoot

Так, проблема в тому , що питання в OP, пов'язаний з колекцією HTML, а НЕ Nodelist ... Так що відповідь була просто не відповісти на питання
Freddo

@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 .


1
Ця відповідь очевидно працює на nodeList. Біда в тому , parent.children повертає колекцію HTML, яка не є Nodelist ...
Freddo

3

Оскільки ви використовуєте функції ES6 ( функції стрілок ), ви також можете просто використовувати цикл типу:

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


Отримано. Яке вивертання, синтаксис ES6, хоча ... мені хочеться плакати, а я йду з C ++ фону ...
Freddo

1

Ви можете перевірити, чи правильно ви ввели forEach , якщо ви ввели foreach, як в інших мовах програмування, це не працюватиме.


0

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

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

або за допомогою оператора спред:

[...parent.children].forEach(function (child) {
    console.log(child)
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.