4

As I've answered questions on this website, I've began to use .forEach() a lot more. I've also started using document.getElementsByClassName(), or document.querySelectorAll() a lot more.

I've recently noticed that sometimes, .forEach() works, and sometimes it doesn't. After a bit of research, I found out that you can't .forEach() through a NodeList. Then I went to this answer and found out that you can .forEach() on a NodeList.

Note: I've also added 2 snippets below in which .forEach() works, and doesn't work. It seems to work for document.querySelectorAll() but not document.getElementsByClassName(), but why? Don't they both return a NodeList?

TL;DR: Why can I .forEach() on some NodeLists, but not all?

.forEach() works on this snippet.

let text = document.querySelectorAll(".text");

console.log(typeof text);
text.forEach(e => {
  console.log(e);
});
<div class="text">Text</div>
<div class="text">Text</div>
<div class="text">Text</div>
<div class="text">Text</div>

.forEach() doesn't work on this snippet.

let text = document.getElementsByClassName("text");

console.log(typeof text);
text.forEach(e => {
  console.log(e);
});
<div class="text">Text</div>
<div class="text">Text</div>
<div class="text">Text</div>
<div class="text">Text</div>
Jack Bashford
  • 43,180
  • 11
  • 50
  • 79
Aniket G
  • 3,471
  • 1
  • 13
  • 39

3 Answers3

2

It's because querySelectorAll returns a NodeList, but getElementsByClassName returns a HTMLCollection:

let querySelector = document.querySelectorAll(".text");
let className = document.getElementsByClassName("text");
console.log(querySelector.constructor.name);
console.log(className.constructor.name);
<div class="text">Text</div>
<div class="text">Text</div>
<div class="text">Text</div>
<div class="text">Text</div>

You have to convert a HTMLCollection to an array before you can iterate over it:

let text = Array.from(document.getElementsByClassName("text"));

text.forEach(e => {
  console.log(e);
});
<div class="text">Text</div>
<div class="text">Text</div>
<div class="text">Text</div>
<div class="text">Text</div>
Jack Bashford
  • 43,180
  • 11
  • 50
  • 79
2

This happens because getElementsByClassName doesn't returns an array but a HTMLCollection. It's an array-like object, as described on the documentation.

If you want to iterate over a HTMLCollection without having to convert anything, you could use:

const list = document.getElementsByClassName('text');
const matches = Array.prototype.forEach.call(list, (el) => console.log(el));

I've added a snippet to clarify this works fine:

const list = document.getElementsByClassName('text');
const matches = Array.prototype.forEach.call(list, (el) => console.log(el));
  <div class="text"></div>
  <div class="text"></div>
  <div class="text"></div>
  <div class="text"></div>

References:

  1. https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection

  2. https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName

Inacio Schweller
  • 1,986
  • 12
  • 22
1

getElementsByClassName returns HTMLCollection which doesn't have a forEach method.

let text1 = document.querySelectorAll(".text");
let text2 = document.getElementsByClassName("text");

console.log(text1.forEach);
console.log(text2.forEach);
<div class="text">Text</div>

Ref: https://developer.mozilla.org/ko/docs/Web/API/Document/getElementsByClassName

zmag
  • 7,825
  • 12
  • 32
  • 42