10

I am trying to assign a click event for each element that has a class name of director__card--iconBox. The way that I am selecting these elements is by getElementsByClassName. With the current code written, I am testing the function to apply a background-color of its parents div to red. The end goal is to be able to flip the div and to display the backside of the div that will come later.

const toggleButton = document.getElementsByClassName("director__card--iconBox");

toggleButton.forEach((el) =>
  el.addEventListener("click", (event) => {
    const card = event.target.parentElement.querySelector(".director__card");
    card.classList.toggle("open");
  })
);



I am getting back an HTMLCollection. My thoughts is that this won't work because of an HTMLCollection? I have tried querySelectorAll to bring back a Node List, yet the NodeList returns empty..

choz
  • 17,242
  • 4
  • 53
  • 73
Felipe
  • 333
  • 7
  • 19
  • Does this answer your question? [JS: iterating over result of getElementsByClassName using Array.forEach](https://stackoverflow.com/questions/3871547/js-iterating-over-result-of-getelementsbyclassname-using-array-foreach) – Heretic Monkey Jul 07 '21 at 18:56

2 Answers2

18

HTMLCollection is an array-like object but it does not have forEach method.

There are multiple ways you can iterate it with;

e.g.

Create an array out of it;

Array.from(toggleButton).forEach((el) =>
  el.addEventListener("click", (event) => {
    const card = event.target.parentElement.querySelector(".director__card");
    card.classList.toggle("open");
  })
);

Use for..of

for (let item of toggleButton) {
    item.forEach((el) =>
        el.addEventListener("click", (event) => {
            const card = event.target.parentElement.querySelector(".director__card");
            card.classList.toggle("open");
        });
    );
}

Or, just for loop.

for (let i = 0, len = toggleButton.length; i < len; i++) {
    toggleButton[i].forEach((el) =>
        el.addEventListener("click", (event) => {
            const card = event.target.parentElement.querySelector(".director__card");
            card.classList.toggle("open");
        });
    );
}
choz
  • 17,242
  • 4
  • 53
  • 73
  • 2
    I don't like "not iterable" when your own example shows `for of` usage. See [Are HTMLCollection and NodeList iterables?](https://stackoverflow.com/questions/31283360/are-htmlcollection-and-nodelist-iterables). – MikeM Jul 07 '21 at 18:49
  • 1
    It has nothing to do with iteratable or not. The `HTMLCollection` interface just doesn't expose a `forEach` method. The `NodeList` interface does. This is also a duplicate question from 11 years ago... – Heretic Monkey Jul 07 '21 at 18:59
  • 1
    It is now a question posted 11 years later. (y) – Felipe Jul 07 '21 at 19:06
  • 1
    @MikeM I was not sure what's best to describe this old artifact. Thanks for pointing that out, I've updated the answer to avoid misunderstanding. – choz Jul 08 '21 at 01:50
  • You have a bug here. In `let i = 0, len = toggleButton.length`, `len` is not a `let` variable. I put this code into a recursively walking function, and it didn't work because of this! The recursive levels assigned to `len`, causing the higher level not to iterate over all the items. – Kaz Jan 19 '22 at 22:43
3

The reason querySelectorAll wasn't returning anything was because you didn't add a . at the start (remember, querySelectorAll accepts a DOMString that must a valid CSS Selector). This caused it return an array of elements with the tag name director__card--iconBox instead of an array of elements with the class director__card--iconBox.

Try the following:

const toggleButton = document.querySelectorAll(".director__card--iconBox");

toggleButton.forEach((el) =>
  el.addEventListener("click", (event) => {
    const card = event.target.parentElement.querySelector(".director__card");
    card.classList.toggle("open");
  })
);
Spectric
  • 30,714
  • 6
  • 20
  • 43