0

I am trying to iterate over the object returned by document.getElementsByClassName(). The returned object contains three elements with the specified class name. However, when I try to access each element using the for loop, the object acts as if it is empty with 0 length.

Here are the console logs I did in an attempt to debug.

First I logged the object returned.

elements = document.getElementsByClassName("brute-force-effect");
console.log(elements);

Output

> HTMLCollection []
  > 0: div.tile.brute-force-effect.harsh
  > 1: div.tile.brute-force-effect.kaso
  > 2: div.name.brute-force-effect
> length: 3
> [[Prototype]]: HTMLCollection

Clearly, it contains all three elements. However, when I logged its length

console.log(elements.length);

Output

0

I tried iterating over it as follow

for (e of elements) {
    console.log(e);
}

Output

No output

And since elements.length returns 0, following method, as provided in the solution to a similar stackoverflow question doesn't work either.

for (i = 0; i < elements.length; i++) {
    console.log(elements.item(i));
}

Output

No output
K450
  • 691
  • 5
  • 17
  • 1
    Notice that in the output, the contents of the collection is empty `[]` in `HTMLCollection []`, it is only the expanded version that shows you the elements. That's because the expanded version shows you the contents of the collection at the time of expanding it in the console (as it gets populated once the DOM loads, since HTMLCollections are "live"). The log itself that shows `[]` is actually the contents at the time of it being logged. See this for more info on your log outputs: [Is Chrome’s JavaScript console lazy about evaluating objects?](https://stackoverflow.com/q/4057440) – Nick Parsons Jul 08 '23 at 08:11
  • @NickParsons I think that clears all my doubts, thank you. – K450 Jul 09 '23 at 05:26

2 Answers2

1

Are you executing your code within the following scope?

document.addEventListener("DOMContentLoaded", function() {
    // Your code here
});
kevP-Sirius
  • 93
  • 1
  • 9
  • It worked, but I do not understand this behaviour, can you explain why this works? – K450 Jul 08 '23 at 04:21
  • Well , usually it is because your html code is dynamically added and you need to wait your page is fully loaded before being able to execute action correctly on your DOM , but your code could have work depending on how fast the client load the DOM but to make it safely we execute all action once it is fully loaded . – kevP-Sirius Jul 08 '23 at 04:24
  • Yes, I get that part, but `document.getElementsByClassName` could still access all the elements from the beginning, which I stored in a variable. Why does iterating over it doesn't work? Does the dynamic nature of HTML update the variable before I iterate over it? – K450 Jul 08 '23 at 04:30
  • You got it , document.getElementsByClassName is executed before your element having the html injected . As an exemple if you put your code within a settimeout function and wait for few sec you will see it will work also because the DOM will be fully loaded – kevP-Sirius Jul 08 '23 at 04:35
  • "Does the dynamic nature of HTML update the variable before I iterate over it?" on initialize your page is empty on client side . your javascript and html aren't 100% sync outside of the scope of the DOMcontentloaded event . so it is more like your javascript run once and your variable elements isn't updated after the execution , so it's stay with an empty array . – kevP-Sirius Jul 08 '23 at 04:46
0

I think the reason why you are unable to iterate over the object returned by document.getElementsByClassName() is because it is an HTMLCollection, not an Array. An HTMLCollection is a live object, which means that it changes as the DOM changes. This can be a problem when you are trying to iterate over an HTMLCollection, because the length of the collection may change while you are iterating over it.

To fix this, you need to convert the HTMLCollection to an Array. You can do this using the Array.from() method. The following code will do this:

const elements = document.getElementsByClassName("x");
const elementsArray = Array.from(elements);

for (const element of elementsArray) {
  console.log(element);
  element.innerHTML = "new text";
}
<html>
  <body>
    <div class="x">1</div>
    <div class="x">2</div>
    <div class="x">3</div>
    <div class="x">4</div>
    <script src="index.js"></script>
  </body>
</html>
  • Thank you for your answer, but I think HTMLCollections are iterable too. The other solution worked for me. I appreciate your help. – K450 Jul 08 '23 at 04:27