259

I'm trying to make a simple loop:

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

But I get the following error:

VM384:53 Uncaught TypeError: parent.children.forEach is not a function

Even though parent.children logs:

enter image description here

What could be the problem?

Note: Here's a JSFiddle.

Yass
  • 2,658
  • 3
  • 13
  • 21
alexchenco
  • 53,565
  • 76
  • 241
  • 413

13 Answers13

222

First option: invoke forEach indirectly

The parent.children is an Array like object. Use the following solution:

const parent = this.el.parentElement;

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

The parent.children is NodeList type, which is an Array like object because:

  • It contains the length property, which indicates the number of nodes
  • Each node is a property value with numeric name, starting from 0: {0: NodeObject, 1: NodeObject, length: 2, ...}

See more details in this article.


Second option: use the iterable protocol

parent.children is an HTMLCollection: which implements the iterable protocol. In an ES2015 environment, you can use the HTMLCollection with any construction that accepts iterables.

Use HTMLCollection with the spread operatator:

const parent = this.el.parentElement;

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

Or with the for..of cycle (which is my preferred option):

const parent = this.el.parentElement;

for (const child of parent.children) {
  console.log(child);
}
Dmitri Pavlutin
  • 18,122
  • 8
  • 37
  • 41
  • When I use your solution I have no more problems, but the code inside the anonymized function is not executed. .so.. – Jérémy Feb 19 '19 at 13:04
  • 3
    Which browser to you use so that parent.children tells you that it is a nodeList. On Firefox, it tells me is an HTMLCollection. If it was a nodeList, .forEach() would work – Freddo Jul 20 '19 at 21:04
187

parent.children is not an array. It is HTMLCollection and it does not have forEach method. You can convert it to the array first. For example in ES6:

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

or using spread operator:

[...parent.children].forEach(function (child) {
    console.log(child)
});
Josh Wood
  • 461
  • 3
  • 14
madox2
  • 49,493
  • 17
  • 99
  • 99
  • 17
    I prefer this solution much more than messing with the Array prototype. – Daut Aug 03 '18 at 14:22
  • And this answer is (one of) the correct answers to the OPs question. parent.children is an HTMLCollection which do not have a .forEach method – Freddo Jul 20 '19 at 21:06
  • I used `Array.from(selected_rows).forEach(item => console.log(item));` in my case and it works – Gloria Chen Oct 22 '21 at 04:23
35

A more naive version, at least you're sure that it'll work on all devices, without conversion and ES6 :

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

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

RBT
  • 24,161
  • 21
  • 159
  • 240
Jean
  • 825
  • 8
  • 17
  • 8
    Upvoted because all these new ES6 functions do exactly the same good old thing that were a available, but in a messy way, as always with JS – Freddo Jul 20 '19 at 20:50
  • 2
    This is way better solution tbh. Its close to other programming languages and has less of a chance for weird JS weirdness. Its simple, no funky things – Marnie Rodriguez Apr 16 '21 at 16:25
25

parent.children will return a node list list, technically a html Collection. That is an array like object, but not an array, so you cannot call array functions over it directly. At this context you can use Array.from() to convert that into a real array,

Array.from(parent.children).forEach(child => {
  console.log(child)
})
Rajaprabhu Aravindasamy
  • 66,513
  • 17
  • 101
  • 130
  • 1
    Nope, parent.children does not return a nodeList but an HTML Collection. Not the same thing. If it was a nodeList, .forEach would work – Freddo Jul 20 '19 at 20:49
10

parent.children is a HTMLCollection which is array-like object. First, you have to convert it to a real Array to use Array.prototype methods.

const parent = this.el.parentElement
console.log(parent.children)
[].slice.call(parent.children).forEach(child => {
  console.log(child)
})
Dmitriy
  • 3,745
  • 16
  • 24
9

You can check if you typed forEach correctly, if you typed foreach like in other programming languages it won't work.

Abdelsalam Megahed
  • 1,281
  • 12
  • 13
7

There is no need for the forEach, you can iterate using only the from's second parameter, like so:

let nodeList = [{0: [{'a':1,'b':2},{'c':3}]},{1:[]}]
Array.from(nodeList, child => {
  console.log(child)
});
Armfoot
  • 4,663
  • 5
  • 45
  • 60
  • The sad news is that parent.children is not a nodeList... .from() won't work. – Freddo Jul 20 '19 at 20:53
  • @Cedric if your object is not a NodeList, then you should ask a new question specifically for addressing it. In here, downvoting is used when the answer is intrinsically wrong or harmful and, as you may see by the code snippet, all the elements of the object are iterated and printed, which was the goal of the OP's question. – Armfoot Jul 22 '19 at 20:45
  • Yep, the problem is that the OP's question related to an HTML Collection, not a nodeList... So the answer was simply not answering the question – Freddo Jul 22 '19 at 21:28
  • 1
    @Cedric this answer will also iterate over an HTML Collection since `Array.from` converts the object given in the first parameter into an array. The result is the same as in [madox2's answer](https://stackoverflow.com/a/35970005/1326147) without needing an extra `forEach` loop ([`Array.from` MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) docs). – Armfoot Jul 24 '19 at 22:37
6

If you are trying to loop over a NodeList like this:

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

I highly recommend loop it this way:

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

Personally, I've tried several ways but most of them didn't work as I wanted to loop over a NodeList, but this one works like a charm, give it a try!

The NodeList isn't an Array, but we treat it as an Array, using Array. So, you need to know that it is not supported in older browsers!

Need more information about NodeList? Please read its documentation on MDN.

Elharony
  • 886
  • 8
  • 18
  • 2
    This answer obviously works on nodeList. The trouble is parent.children returns an HTML Collection, which is not a nodeList... – Freddo Jul 20 '19 at 20:54
5

That's because parent.children is a NodeList, and it doesn't support the .forEach method (as NodeList is an array like structure but not an array), so try to call it by first converting it to array using

var children = [].slice.call(parent.children);
children.forEach(yourFunc);
Ammar Hasan
  • 2,436
  • 16
  • 22
3

Since you are using features of ES6 (arrow functions), you may also simply use a for loop like this:

for(let child of [{0: [{'a':1,'b':2},{'c':3}]},{1:[]}]) {
  console.log(child)
}
Armfoot
  • 4,663
  • 5
  • 45
  • 60
  • Upvoted. What a contortion, the ES6 syntax, though... Makes me want to cry, and I'm coming from a C++ background... – Freddo Jul 20 '19 at 21:00
1

use JSON.parse()

str_json = JSON.parse(array);
str_json.forEach(function (item, index) {
    console.log(item);
});
kometen
  • 6,536
  • 6
  • 41
  • 51
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details like the reason why the proposed code is the correct solution – leugim Oct 30 '21 at 19:53
0

You can use childNodes instead of children, childNodes is also more reliable considering browser compatibility issues, more info here:

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

or using spread operator:

[...parent.children].forEach(function (child) {
    console.log(child)
});
Syed
  • 15,657
  • 13
  • 120
  • 154
0

for object try this:

  Object.keys(yourObj).forEach(key => {
    console.log(key, yourObj[key]);
  });
Gigoland
  • 1,287
  • 13
  • 10