Search code examples
javascriptcssonclicklistener

Close dropdown menu on <li> click with vanilla javascript


I have a dropdown menu that has a transition and it works exactly how I wanted. I want to keep the transition how it is. The dropdown menu also has options for users to filter the videos on display on the page.

The issue I am having is that I want the menu to close once a user clicks any of the < li > selections. At the moment it only closes again using the main button.

I have tried a few different options but my Javascript isn't quite strong enough yet to know where I'm going wrong and I really don't want to use JQuery or Bootstrap.

Any help would be greatly appreciated!

// Video Filter
var filterItems = document.querySelectorAll('.item')

function showAll() {
    filterItems.forEach(function(element) {
        element.classList.add('show')
    })
}

function showCategory(category) {
    filterItems.forEach(function(element){
        if (element.classList.contains(category)) {
            element.classList.add('show')
        }
        else {
            element.classList.remove('show')
        }
    })
}
showAll()

//  Dropdown Button
var filterBtn = document.getElementsByClassName("dropbtn");
var i;

for (i = 0; i < filterBtn.length; i++) {
  filterBtn[i].addEventListener("click", function() {
    this.classList.toggle("active");
    var content = this.nextElementSibling;
    if (content.style.maxHeight){
      content.style.maxHeight = null;
    } else {
      content.style.maxHeight = content.scrollHeight + "px";
    } 
  })
}
.dropbtn {
  position: relative;
  left: 50%;
  transform: translateX(-50%);  
  text-decoration: none;
  padding: 20px 20px 18px 20px;
  color: black;
  background-color: rgb(245, 235, 244);
  border-radius: 100px;
  display: inline-block;
  cursor: pointer;
  font-weight: 400;
  letter-spacing: 1px;
  text-transform: uppercase;
  border: none;
}

.dropdown {
  position: relative;
  display: inline-block;
  left: 50%;
  transform: translateX(-50%);  
  margin-bottom: 20px;
}

.dropdown-content {
  position: relative;  
  background-color: rgba(245, 235, 244);
  min-width: 160px;
  z-index: 1;
  margin: 0;
  list-style-type: none;
  font-weight: 400;
  letter-spacing: 1px;
  text-transform: uppercase;
  padding: 0 10px;
  text-align: center; 

  max-height: 0;
  overflow: hidden;
  transition: max-height 0.25s ease-out;
}

.dropdown-content li {
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: flex;
  flex-direction: column;
  justify-content: center;
  cursor: pointer;
}


.dropdown-content li:hover {filter: opacity(60%);}
.dropbtn:hover {opacity: 60%;}
.dropbtn:focus {outline-color: white;}


.video-main h2 {
  font-weight: 600;
  text-transform: uppercase;
  font-size: 1.8em;
  letter-spacing: 1.5px;
  margin: 30px 0;
  color: rgb(245, 235, 244);
  align-self: center;
}

.video-main {
  display: flex;
  flex-direction: column;
  align-content: center;
  background-color: black;
}

.video-boxes {
  display: flex;
  width: 100%;
  justify-content: space-evenly;
  flex-direction: row;
  flex-wrap: wrap;
  text-align: center;
  
}

.video-boxes video {
  width: 100%;
  max-width: 1280px;
  max-height: 40vh;
}

.item {
  display: none;
  width: 90%;
  flex-wrap: wrap;
  text-align: center;
  margin-bottom: 20px;
}


.video-boxes .item h4 {
  color: rgb(245, 235, 244);
  font-weight: 400;
  letter-spacing: 2px;
  text-transform: uppercase;
}

.show {
  display: block;  
}
<main class="video-main">
    <h2>Video</h2>
      <div class="dropdown">
        <button class="dropbtn">Filter Projects</button>
        <ul class="dropdown-content">
            <li class="filter-list-item" id="all" onclick="showAll()">All Items</li>
            <li class="filter-list-item" onclick="showCategory('category-nike')">Nike</li>
            <li class="filter-list-item" onclick="showCategory('category-BSpace')">BlankSpace</li>
            <li class="filter-list-item" onclick="showCategory('category-AfterEff')">After Effects</li>
            <li class="filter-list-item" onclick="showCategory('category-vegan')">Vegan</li>
          </ul>
      </div>
    <div class="video-boxes">
      <div class="item category-nike">
        <video src="" controls></video>
        <h4 class="video-title">Nike</h4>
      </div>
      <div class="item category-BSpace">
        <video src="" controls></video>
        <h4 class="video-title">Blankspace</h4>
      </div>
       <div class="item category-AfterEff">
        <video src="" controls></video>
        <h4 class="video-title">Logo Animation</h4>
      </div>
      <div class="item category-vegan">
        <video src="" controls></video>
        <h4 class="video-title">Vegan</h4>
      </div>
     </div>
    </main>


Solution

  • You need to set the max-height for your list items container to zero when one of the list items is clicked. You also need to reset the filter button by removing the active class from it. First define a variable for your list items:

    var listItems = document.querySelectorAll('.filter-list-item');
    

    Then add an event listener for each list item, which would collapse the list items container on click:

    listItems.forEach(item => {
      item.addEventListener("click", function() {
        filterBtn[0].classList.remove("active");
        var content = filterBtn[0].nextElementSibling;
        content.style.maxHeight = "";
      });
    });
    

    The full code:

    // Video Filter
    var filterItems = document.querySelectorAll('.item');
    var listItems = document.querySelectorAll('.filter-list-item');
    
    function showAll() {
      filterItems.forEach(function(element) {
        element.classList.add('show')
      })
    }
    
    function showCategory(category) {
      filterItems.forEach(function(element) {
        if (element.classList.contains(category)) {
          element.classList.add('show')
        } else {
          element.classList.remove('show')
        }
      })
    }
    showAll()
    
    //  Dropdown Button
    var filterBtn = document.getElementsByClassName("dropbtn");
    var i;
    
    for (i = 0; i < filterBtn.length; i++) {
      filterBtn[i].addEventListener("click", function() {
        this.classList.toggle("active");
        var content = this.nextElementSibling;
        if (content.style.maxHeight) {
          content.style.maxHeight = null;
        } else {
          content.style.maxHeight = content.scrollHeight + "px";
        }
      })
    }
    
    listItems.forEach(item => {
      item.addEventListener("click", function() {
        filterBtn[0].classList.remove("active");
        var content = filterBtn[0].nextElementSibling;
        content.style.maxHeight = "";
      });
    });
    .dropbtn {
      position: relative;
      left: 50%;
      transform: translateX(-50%);
      text-decoration: none;
      padding: 20px 20px 18px 20px;
      color: black;
      background-color: rgb(245, 235, 244);
      border-radius: 100px;
      display: inline-block;
      cursor: pointer;
      font-weight: 400;
      letter-spacing: 1px;
      text-transform: uppercase;
      border: none;
    }
    
    .dropdown {
      position: relative;
      display: inline-block;
      left: 50%;
      transform: translateX(-50%);
      margin-bottom: 20px;
    }
    
    .dropdown-content {
      position: relative;
      background-color: rgba(245, 235, 244);
      min-width: 160px;
      z-index: 1;
      margin: 0;
      list-style-type: none;
      font-weight: 400;
      letter-spacing: 1px;
      text-transform: uppercase;
      padding: 0 10px;
      text-align: center;
      max-height: 0;
      overflow: hidden;
      transition: max-height 0.25s ease-out;
    }
    
    .dropdown-content li {
      color: black;
      padding: 12px 16px;
      text-decoration: none;
      display: flex;
      flex-direction: column;
      justify-content: center;
      cursor: pointer;
    }
    
    .dropdown-content li:hover {
      filter: opacity(60%);
    }
    
    .dropbtn:hover {
      opacity: 60%;
    }
    
    .dropbtn:focus {
      outline-color: white;
    }
    
    .video-main h2 {
      font-weight: 600;
      text-transform: uppercase;
      font-size: 1.8em;
      letter-spacing: 1.5px;
      margin: 30px 0;
      color: rgb(245, 235, 244);
      align-self: center;
    }
    
    .video-main {
      display: flex;
      flex-direction: column;
      align-content: center;
      background-color: black;
    }
    
    .video-boxes {
      display: flex;
      width: 100%;
      justify-content: space-evenly;
      flex-direction: row;
      flex-wrap: wrap;
      text-align: center;
    }
    
    .video-boxes video {
      width: 100%;
      max-width: 1280px;
      max-height: 40vh;
    }
    
    .item {
      display: none;
      width: 90%;
      flex-wrap: wrap;
      text-align: center;
      margin-bottom: 20px;
    }
    
    .video-boxes .item h4 {
      color: rgb(245, 235, 244);
      font-weight: 400;
      letter-spacing: 2px;
      text-transform: uppercase;
    }
    
    .show {
      display: block;
    }
    <main class="video-main">
      <h2>Video</h2>
      <div class="dropdown">
        <button class="dropbtn">Filter Projects</button>
        <ul class="dropdown-content">
          <li class="filter-list-item" id="all" onclick="showAll()">All Items</li>
          <li class="filter-list-item" onclick="showCategory('category-nike')">Nike</li>
          <li class="filter-list-item" onclick="showCategory('category-BSpace')">BlankSpace</li>
          <li class="filter-list-item" onclick="showCategory('category-AfterEff')">After Effects</li>
          <li class="filter-list-item" onclick="showCategory('category-vegan')">Vegan</li>
        </ul>
      </div>
      <div class="video-boxes">
        <div class="item category-nike">
          <video src="" controls></video>
          <h4 class="video-title">Nike</h4>
        </div>
        <div class="item category-BSpace">
          <video src="" controls></video>
          <h4 class="video-title">Blankspace</h4>
        </div>
        <div class="item category-AfterEff">
          <video src="" controls></video>
          <h4 class="video-title">Logo Animation</h4>
        </div>
        <div class="item category-vegan">
          <video src="" controls></video>
          <h4 class="video-title">Vegan</h4>
        </div>
      </div>
    </main>