Search code examples
javascriptdrop-down-menu

click on one drop down menu and close all other drop downs (vanilla javascript)


Hi I am new to vanilla JavaScript and trying to get a good understanding of it. I've created a multiple drop down menu navigation where the menus are triggered by a click event when the drop down buttons are clicked, a .show CSS class with the display property of block is added to the drop menu. The problem I'm having is that when I have one menu open and I click on another drop menu button, I want the current/all other menus to close. I'm not sure quite how I would achieve this. Any help is greatly appreciated, thanks.

Here is the JS:

(function() {

  var dropBtns = document.querySelectorAll('.dropdown');

  dropBtns.forEach(function(btn) {

    btn.addEventListener('click', function(e) {
      var dropContent = btn.querySelector('.dropMenu');
      e.preventDefault();

      if (!dropContent.classList.contains('show')) {
        dropContent.classList.add('show');
      } else {
        dropContent.classList.remove('show');
      }
      e.stopPropagation();
    });


  });

  //   close menus when clicking outside of them
  window.addEventListener('click', function(event) {
    if (event.target != dropBtns) {
      openMenus = document.querySelectorAll('.dropMenu');
      openMenus.forEach(function(menus) {
        menus.classList.remove('show');
      });
    }
  });

})();

Here is the CSS:

.room-sort {
  background: #434A54;
  margin: 0;
  padding: 0;
  text-align: center;
}

.room-sort-menu ul {
  margin: 0;
  padding: 0;
}

span.sort {
  margin-right: 30px;
  color: #fff;
  font-weight: 500;
}

.sort-mobile {
  display: none;
}

.room-sort-menu>li {
  display: inline-block;
  color: #fff;
}

.room-sort-menu>li>a {
  display: inline-block;
  padding: 16px 30px;
  margin: 0;
  font-size: 0.8em;
  color: #fff;
  text-decoration: none;
}

.room-sort-menu>li>a:hover,
.room-sort-menu>li>a:focus {
  background: #2e333a;
}

.dropdown {
  position: relative;
}

.dropMenu {
  position: absolute;
  display: none;
  top: 46px;
  left: 0px;
  border: 1px solid color;
  width: 109px;
  background: #CB242D;
  font-size: 0.8em;
  z-index: 1;
}

.show {
  display: block;
}

.room-sort-menu li:last-of-type ul.dropMenu {
  width: 166px;
}

.dropMenu li a {
  display: block;
  padding: 16px 20px;
  color: #fff;
  text-decoration: none;
}

.dropMenu li a:hover {
  background: #a0080d;
}

Here is the HTML:

<div class="room-sort">

  <ul class="room-sort-menu">

    <span class="sort">Sort by :</span>
    <li class="dropdown"><a class="dropBtn" href="#">Price <i class="fa fa-caret-down" aria-hidden="true"></i></a>
      <ul class="dropMenu">
        <li><a href="#">Price (hi to low)</a></li>
        <li><a href="#">Price (low to hi)</a></li>
      </ul>
    </li>
    <li class="dropdown"><a class="dropBtn" href="#">Stars <i class="fa fa-caret-down" aria-hidden="true"></i></a>
      <ul class="dropMenu">
        <li><a href="#">Stars (hi to low)</a></li>
        <li><a href="#">Stars (low to hi)</a></li>
      </ul>
    </li>

    <li class="dropdown"><a class="dropBtn" href="#">Review score <i class="fa fa-caret-down" aria-hidden="true"></i></a>
      <ul class="dropMenu">
        <li><a href="#">Score (hi to low)</a></li>
        <li><a href="#">Score (low to hi)</a></li>
      </ul>
    </li>
  </ul>
</div>

Solution

  • You were almost there, just moved the code around a bit and it works as intended.

    (function() {
    
      var dropBtns = document.querySelectorAll('.dropdown');
    
      function closeOpenItems() {
          openMenus = document.querySelectorAll('.dropMenu');
          openMenus.forEach(function(menus) {
            menus.classList.remove('show');
          });  
      }
    
      dropBtns.forEach(function(btn) {
    
        btn.addEventListener('click', function(e) {
          var 
            dropContent = btn.querySelector('.dropMenu'),
            shouldOpen = !dropContent.classList.contains('show');
          e.preventDefault();
    
          // First close all open items.
          closeOpenItems();
          // Check if the clicked item should be opened. It is already closed at this point so no further action is required if it should be closed.
          if (shouldOpen) {
            // Open the clicked item.
            dropContent.classList.add('show');      
          }
          e.stopPropagation();
        });
    
    
      });
    
      //   close menus when clicking outside of them
      window.addEventListener('click', function(event) {
        if (event.target != dropBtns) {
          // Moved the code here to its own function.
          closeOpenItems();
        }
      });
    
    })();
    .room-sort {
      background: #434A54;
      margin: 0;
      padding: 0;
      text-align: center;
    }
    
    .room-sort-menu ul {
      margin: 0;
      padding: 0;
    }
    
    span.sort {
      margin-right: 30px;
      color: #fff;
      font-weight: 500;
    }
    
    .sort-mobile {
      display: none;
    }
    
    .room-sort-menu>li {
      display: inline-block;
      color: #fff;
    }
    
    .room-sort-menu>li>a {
      display: inline-block;
      padding: 16px 30px;
      margin: 0;
      font-size: 0.8em;
      color: #fff;
      text-decoration: none;
    }
    
    .room-sort-menu>li>a:hover,
    .room-sort-menu>li>a:focus {
      background: #2e333a;
    }
    
    .dropdown {
      position: relative;
    }
    
    .dropMenu {
      position: absolute;
      display: none;
      top: 46px;
      left: 0px;
      border: 1px solid color;
      width: 109px;
      background: #CB242D;
      font-size: 0.8em;
      z-index: 1;
    }
    
    .show {
      display: block;
    }
    
    .room-sort-menu li:last-of-type ul.dropMenu {
      width: 166px;
    }
    
    .dropMenu li a {
      display: block;
      padding: 16px 20px;
      color: #fff;
      text-decoration: none;
    }
    
    .dropMenu li a:hover {
      background: #a0080d;
    }
    <div class="room-sort">
    
      <ul class="room-sort-menu">
    
        <span class="sort">Sort by :</span>
        <li class="dropdown"><a class="dropBtn" href="#">Price <i class="fa fa-caret-down" aria-hidden="true"></i></a>
          <ul class="dropMenu">
            <li><a href="#">Price (hi to low)</a></li>
            <li><a href="#">Price (low to hi)</a></li>
          </ul>
        </li>
        <li class="dropdown"><a class="dropBtn" href="#">Stars <i class="fa fa-caret-down" aria-hidden="true"></i></a>
          <ul class="dropMenu">
            <li><a href="#">Stars (hi to low)</a></li>
            <li><a href="#">Stars (low to hi)</a></li>
          </ul>
        </li>
    
        <li class="dropdown"><a class="dropBtn" href="#">Review score <i class="fa fa-caret-down" aria-hidden="true"></i></a>
          <ul class="dropMenu">
            <li><a href="#">Score (hi to low)</a></li>
            <li><a href="#">Score (low to hi)</a></li>
          </ul>
        </li>
      </ul>
    
    </div>