skip to Main Content

Four months into javascript, and I ran into the following code that I can’t put in terms of the techniques that I understand. I was hoping that someone could refactor this code not in order to satisfy what its purpose is, but to put it in terms that I would understand. Perhaps just explaining how the forEach() works with call(). I understand them separately but not combined.

I have tried to rewrite it without the forEach and call but that defeats my purpose. I can implement this functionality without these two, but it sure makes the code succinct with them.

let parentElement = document.getElementById("node1");
let array = [];
parentElement.addEventListener("click", (e) => {
  var elems = document.querySelectorAll(".active"); // make them all active 
  array.forEach.call(elems, function(el) {
    el.classList.remove("active"); // remove active
  });
  e.target.className = "active"; // set clicked element to active
});
.active,
.btn:hover {
  background-color: #666;
  color: white;
}
<div id="node1">
  <button class="btn">1</button>
  <button class="btn">2</button>
</div>

2

Answers


  1. This code pattern is a bit dated. I’ll explain how it works, and then show you a modern method.

    var elems = document.querySelectorAll(".active"); // make them all active 
    

    The value of elems at this point is a NodeList. It is not an Array.

    Back in the day, you couldn’t use .forEach() on a NodeList. Therefore, you needed to borrow that method from Array. (It was added later, but for this example let’s assume it doesn’t exist yet.)

    array.forEach.call(elems, function (el) {…});
    

    This line allows you to use Array.forEach() over the elems NodeList. You’re executing/calling the array.forEach function, with elems as the "this" object for context.

    The Modern Way

    These days, you can use for … of, which allows you to iterate over iterables like NodeList, Arrays, and many others.

    for (const el of document.querySelectorAll('.active')) {
      el.classList.remove('active');
    }
    

    I think you’ll find this syntax easier to read and understand.

    (By the way, have you considered that radio button inputs might fit better for your use in this case? You can style them to look like buttons.)

    Login or Signup to reply.
  2. We no longer need Array.call.

    Also var elems = document.querySelectorAll(".active"); // make them all active does not make all active, but just selects the active ones

    All modern browsers can do .forEach on the static (not live) NodeList returned from querySelectorAll (you need to spread to use map and filter line this: [...elems].map(el => something(el)))

    Here is the more concise modern way to do want you want – note the classList.toggle with the boolean to force the class when true

    document.getElementById("node1").addEventListener("click", (e) => {
      const tgt = e.target;
      document.querySelectorAll(".btn")
        .forEach(el => el.classList.toggle("active",el === tgt)); 
    });
    .active,
    .btn:hover {
      background-color: #666;
      color: white;
    }
    <div id="node1">
      <button class="btn">1</button>
      <button class="btn">2</button>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search