Skip to content

Click Event Shows And Removes All Overlays Instead Of Individually – JavaScript

I have a wrapper container where I would like is so when I click on the name each piece of info shows just for that wrapper and when I click/close the ‘x’ button it only removes it for that button.

Using the forEach() method it is showing and removing all of the inner containers except for just the ones that have been clicked.

I thought it might be possible doing with the this keyword but I couldn’t get it to work.

I’m new to Javascript so any help would be hugely appreciated.

Codepen: https://codepen.io/anna_paul/pen/JjWPLjx

window.addEventListener('DOMContentLoaded', function() {
  
  let name = document.querySelectorAll('.name')
  let close = document.querySelectorAll('.close')
  let innerText = document.querySelectorAll('.inner-text')

  // ----- show text
  name.forEach(function(item){

      item.addEventListener('click', function(){

          innerText.forEach(function(inner){
              inner.classList.add('active')
          })

      }, false)

  })

  // ---- hide text
  close.forEach(function(item){

      item.addEventListener('click', function(){

          innerText.forEach(function(inner){
             inner.classList.remove('active')
          })

      }, false)

  })
  
}) // DomContentLoaded
* {position: relative; box-sizing: border-box;}

body {
  margin: 0;
  height: 100vh;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.wrapper {
  width: 10rem;
  background: #fafafa;
  padding: 1rem;
  margin: 1rem;
}

.inner-text {
  background: red;
  display:none;
}

.inner-text.active {
    display: block
}

.close {
    position: absolute;
    top: -5rem;
    right: 0rem;
    padding: .5rem;
    background: white;
    z-index: 2;
}

.name {
  background: yellow;
  padding: 1rem;
  cursor: pointer;
}
<div class="wrapper">
  <p class="name">Name</p>
  <div>Other content</div>
  <div class="inner-text">
    <div class="close">x</div>
    <ul>
      <li>INFO</li>
      <li>INFO</li>
      <li>INFO</li>
      <li>INFO</li>
      <li>INFO</li>
    </ul>
  </div>
</div>

<div class="wrapper">
  <p class="name">Name</p>
  <div>Other content</div>
  <div class="inner-text">
    <div class="close">x</div>
    <ul>
      <li>INFO</li>
      <li>INFO</li>
      <li>INFO</li>
      <li>INFO</li>
      <li>INFO</li>
    </ul>
  </div>
</div>

Answer

You’ve pretty much identified the issue in your question which is that you are looping over all the panels in your event handlers and adding/removing classes to all the panels. Instead, you can work with “event delegation” where you set up just one event handler at a common ancestor of all the panels and let the event “bubble” up to that ancestor and be handled there. Also, because the code for the show and the hide are so similar, you can just have one function for both.

Lastly, innerText is not a great name for a variable as innerText is actually a DOM element property name.

// If you place the script that holds this code just before the
// closing BODY tag, you won't need to set up a DOMContentLoaded
// event.

document.querySelector(".masterWrapper").addEventListener("click", function(event){
  // Check to see if the event originated at an element
  // we care about handling
  
  // Get a reference to the <div class="wrapper"> ancestor of the clicked element
  // and then find the <div class="inner-text"> descedant within it.
  const inner_text = event.target.closest(".wrapper").querySelector(".inner-text");
  
  // When Name is clicked
  if(event.target.classList.contains("name")){
    // If the panel is not already showing its content:
    if(!inner_text.classList.contains("active")){
      inner_text.classList.add("active");
    }
  }
  
  // When the X is clicked
  if(event.target.classList.contains("close")){
      event.target.parentElement.classList.remove("active");
  }  

});
* {position: relative; box-sizing: border-box;}

body {
  margin: 0;
  height: 100vh;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.wrapper {
  width: 10rem;
  background: #fafafa;
  padding: 1rem;
  margin: 1rem;
}

.inner-text {
  background: red;
  display:none;
}

.inner-text.active {
    display: block
}

.close {
    position: absolute;
    top: -5rem;
    right: 0rem;
    padding: .5rem;
    background: white;
    z-index: 2;
}

.name {
  background: yellow;
  padding: 1rem;
  cursor: pointer;
}
<div class="masterWrapper">
  <div class="wrapper">
    <p class="name">Name</p>
    <div>Other content</div>
    <div class="inner-text">
      <div class="close">x</div>
      <ul>
        <li>INFO</li>
        <li>INFO</li>
        <li>INFO</li>
        <li>INFO</li>
        <li>INFO</li>
      </ul>
    </div>
  </div>

  <div class="wrapper">
    <p class="name">Name</p>
    <div>Other content</div>
    <div class="inner-text">
      <div class="close">x</div>
      <ul>
        <li>INFO</li>
        <li>INFO</li>
        <li>INFO</li>
        <li>INFO</li>
        <li>INFO</li>
      </ul>
    </div>
  </div>
</div>