Search code examples
javascriptreturnaddeventlistener

How do I return or make null all other EventListeners if another is triggered using vanilla Javascript


What I am trying to accomplish is a code that when one of the buttons is clicked, the button gains the styles listed below and all other button styles return to null.

I assume it involves a boolean statement, but perhaps I am wrong.

You'll see below that I am attempting to use my "else if" statement to establish that when the event target does not equal "button", to return everything to null. However, that is clearly not working.

I have also tried "!==" and that didn't work.

Obviously, this can be done by creating an Eventlistener unique to each button, but I wanted to figure out how to do this dynamically.

CSS:

button {
  font-family: "sans-serif";
  margin: 1%;
  padding: 1%;
  cursor: pointer;
  font-size: 22px;
}

Here is my html:

<section id="button-container">
  <button>a</button>
  <button>b</button>
  <button>c</button>
  <button>d</button>
  <button>e</button>
</section>

Here is my Javascript:

var buttons = document.querySelectorAll('#button-container button');

buttons.forEach(button => {
  button.addEventListener("click", function(e) {
    if (e.target === button) {
      e.target.style.backgroundColor = "blue";
      e.target.style.color = "orange";
    } else if (e.target != button) {
      e.target.style.backgroundColor = null;
      e.target.style.color = null;
    }
  })
})

Solution

  • Instead of modifying the style directly I would use a class instead:

    button.active {
      background-color: blue;
      color: orange
    }
    

    When you click a button then add the active class to it:

    button.addEventListener("click", function(e) {
      this.classList.add("active");
    });
    

    We now only have to add some logic to remove the active class from the previously clicked button.

    We could either go over all the buttons and remove the class from all of them:

    buttons.forEach(button => button.classList.remove("active"));
    

    Or we query the DOM for the currently "active" button and remove it from that button only:

    const activeButton = document.querySelector("#button-container button.active");
    activeButton.classList.remove("active");
    

    or we use the parentNode from the current button to "search":

    const activeButton = button.parentNode.querySelector("button.active");
    

    Or we use the "magic" of .getElementsByClassName() which returns a live collection (it will automatically update with the current set of elements with the given class) of all elements with a given CSS class:

    const buttons = document.querySelector('#button-container');
    const activeButtons = document.querySelector("#button-container").getElementsByClassName("active");
    
    /* ... */
    
    button.addEventListener("click", function(ev) {
      if (activeButtons.length) {
        activeButtons[0].classList.remove("active");  // there should always be only _one_ active button
      }
    
      this.classList.add("active");
    });
    

    Example:

    var buttons = document.querySelectorAll('#button-container button');
    
    buttons.forEach(button => {
      button.addEventListener("click", function(e) {
        const activeButton = button.parentNode.querySelector("button.active");
        if (activeButton) {
          activeButton.classList.remove("active");
        }
        
        this.classList.add("active");
      })
    })
    button {
      font-family: "sans-serif";
      margin: 1%;
      padding: 1%;
      cursor: pointer;
      font-size: 22px;
    }
    
    button.active {
      background-color: blue;
      color: orange;
    }
    <section id="button-container">
      <button>a</button>
      <button>b</button>
      <button>c</button>
      <button>d</button>
      <button>e</button>
    </section>