Home > front end >  How to get dropdown menu to close when user clicks anywhere else on the page
How to get dropdown menu to close when user clicks anywhere else on the page

Time:12-10

I have this dropdown menu of multiple different bubbles and I want the menu that drops down underneath the bubbles to close when I click elsewhere, the last line of JS code that I provided in my example was my attempt at making this work but it seems to not be working for some reason. I included the total code for what I'm doing.

function toggleVisibility(link) {
  // get the corresponding list
  var list = link.parentElement.nextElementSibling;

  // toggle the visibility of the list
  if (list.style.display === "none") {
    // hide all other lists
    var otherLists = document.querySelectorAll(".dropdown-list-models");
    for (var i = 0; i < otherLists.length; i  ) {
      otherLists[i].style.display = "none";
    }

    // position the selected list below the previous list
    list.style.top = (link.parentElement.offsetTop   link.parentElement.offsetHeight)   "px";

    // show the selected list
    list.style.display = "block";
  } else {
    list.style.display = "none";
  }
}
var container = document.querySelector(".dropdown-container-models");
container.addEventListener("click", function() {
var list = this.querySelector("ul");
if (list.style.display === "none") {
  list.style.display = "block";
} else {
  list.style.display = "none";
}
});
// add event listeners to each list item
var listItems = document.querySelectorAll(".dropdown-container-models ul li");
listItems.forEach(function(listItem) {
listItem.addEventListener("click", function() {
  var link = this.querySelector("a");
  window.location.href = link.href;
});
});

// Get the dropdown menu
var dropdownMenu = document.getElementById("myDropdownMenu");

// When the user clicks anywhere outside of the dropdown menu, close it
window.onclick = function(event) {
if (!event.target.matches('.dropdown-list-models')) {
dropdownMenu.classList.remove('show');
}
}
a {
    color: white;
    text-decoration: none;/* Change this to the desired color */
    transition: #04AA6D 0.2s;
}

.dropdown-container-models {
  position: relative; /* create a positioning context for the list items */
  background-color: #333;
    color: white;
    text-decoration: none;
    width: 150px;
    text-align: center;
    transition: background-color 0.2s;
    display: inline-block;
    cursor: pointer;
    font-family: Arial, Helvetica, sans-serif;
    border-radius: 10px;
}

.dropdown-container-models ul li {
  cursor: pointer;
}

.dropdown-list-models {
  position: absolute; /* position the list items absolutely */
  display: none; /* hide the list by default */
  top: 100px; /* hide the list by default */
  list-style-type: none; /* remove the bullet points for the list items */
  padding: 0; /* remove the padding for the list */
  left: 0; /* position the list items at the left edge of the list */
  transition: border 0.2s; /* add a transition effect for the border */
  background-color: #b0b0b0;
  color: black;
  text-decoration: none;
  width: 150px;
  text-align: center;
  transition: background-color 0.2s;
  font-family: Arial, Helvetica, sans-serif;
  border-radius: 10px;
}
.dropdown-list-models.show {
  display: block; /* Show the menu when the .show class is added */
}

.dropdown-list-models li:hover {
  background-color: #04AA6D;
  transition: background-color 0.2s;
  border-radius: 10px;/* change the background color of the list items to green when they are hovered over */
}
.dropdown-list-models li {
  color: black; /* set the color of the list items to black */
  text-align: center;
  transition: #04AA6D 0.2s;
}
<div >
  <p><a href="#" onclick="toggleVisibility(this)">Global Models</a></p>
  <ul  id="myDropdownMenu">
    <li><a href="#">GFS</a></li>
    <li><a href="#">CMC</a></li>
    <li><a href="#">ECMWF</a></li>
  </ul>
</div>
<div >
  <p><a href="#" onclick="toggleVisibility(this)">Mesoscale Models</a></p>
  <ul  id="myDropdownMenu">
      <li><a href="#">NAM 12km</a></li>
    <li><a href="#">NAM 3km</a></li>
    <li><a href="#">HRRR</a></li>
      <li><a href="#">HREF</a></li>
      <li><a href="#">NBM</a></li>
  </ul>
</div>
<div >
  <p><a href="#" onclick="toggleVisibility(this)">Ensembles</a></p>
  <ul  id="myDropdownMenu">
      <li><a href="#">GEFS</a></li>
    <li><a href="#">GEPS</a></li>
    <li><a href="#">EPS</a></li>
  </ul>
</div>
<div >
  <p><a href="#" onclick="toggleVisibility(this)">Super Ensemble</a></p>
</div>   

CodePudding user response:

I'm sure there are several ways of doing this, but the tricky thing is that elements like <ul> do not have a blur event. Because of this, you have to sort of manually detect when the user clicks outside of an element.

I added 2 functions that basically utilize the elementsFromPoint() method and an event listener on the body. With this, you can get a list of all elements located at the x/y positions of the mouse when the user clicks anywhere in the document. Then you just check to see if your dropdown element is at the clicked location, if not you can trigger some other action (passed in as a function), like hiding the dropdown.

I also took out a bit of your original code because it wasn't really necessary. It seemed like you had multiple method of showing/hiding the dropdowns all in the code together, which made for some weird results. So here is a simplified version with the added blur detection:

document.querySelectorAll(".dropdown-container-models").forEach(d => {
  d.addEventListener("click", e => {
    const dropdown = e.target.closest(".dropdown-container-models").querySelector("ul")
    if(dropdown) {
      dropdown.style.display = "block"
      _ElementBlur(e.target, () => {
        dropdown.style.display = "none"
      })
    }
  })
})

document.querySelectorAll(".dropdown-container-models ul li").forEach(listItem => {
  listItem.addEventListener("click", e => {
    window.location.href = e.target.querySelector("a").href
  })
})

// Functions for getting a 'blur' event on an element
const _ElementBlur = (el, f = false) => {
  const ac = new AbortController()
  document.body.addEventListener("click", _CheckBlur(el, f, ac), { signal: ac.signal })
}

const _CheckBlur = (el, f = false, ac) => {
  return e => {
    if(!document.elementsFromPoint(e.clientX, e.clientY).filter(efp => efp == el).length || !document.elementsFromPoint(e.clientX, e.clientY).length) {
      if(f && typeof(f) == "function") f()
      ac.abort()
    }
  }
}
html, body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
}

a {
  color: white;
  text-decoration: none;/* Change this to the desired color */
  transition: #04AA6D 0.2s;
}

.dropdown-container-models {
  position: relative; /* create a positioning context for the list items */
  background-color: #333;
  color: white;
  text-decoration: none;
  width: 150px;
  text-align: center;
  transition: background-color 0.2s;
  display: inline-block;
  cursor: pointer;
  font-family: Arial, Helvetica, sans-serif;
  border-radius: 10px;
}

.dropdown-container-models ul li {
  cursor: pointer;
}

.dropdown-list-models {
  position: absolute; /* position the list items absolutely */
  display: none; /* hide the list by default */
  top: 100px; /* hide the list by default */
  list-style-type: none; /* remove the bullet points for the list items */
  padding: 0; /* remove the padding for the list */
  left: 0; /* position the list items at the left edge of the list */
  transition: border 0.2s; /* add a transition effect for the border */
  background-color: #b0b0b0;
  color: black;
  text-decoration: none;
  width: 150px;
  text-align: center;
  transition: background-color 0.2s;
  font-family: Arial, Helvetica, sans-serif;
  border-radius: 10px;
}
.dropdown-list-models.show {
  display: block; /* Show the menu when the .show class is added */
}

.dropdown-list-models li:hover {
  background-color: #04AA6D;
  transition: background-color 0.2s;
  border-radius: 10px;/* change the background color of the list items to green when they are hovered over */
}
.dropdown-list-models li {
  color: black; /* set the color of the list items to black */
  text-align: center;
  transition: #04AA6D 0.2s;
}
<div >
  <p><a href="#">Global Models</a></p>
  <ul >
    <li><a href="#">GFS</a></li>
    <li><a href="#">CMC</a></li>
    <li><a href="#">ECMWF</a></li>
  </ul>
</div>
<div >
  <p><a href="#">Mesoscale Models</a></p>
  <ul >
    <li><a href="#">NAM 12km</a></li>
    <li><a href="#">NAM 3km</a></li>
    <li><a href="#">HRRR</a></li>
    <li><a href="#">HREF</a></li>
    <li><a href="#">NBM</a></li>
  </ul>
</div>
<div >
  <p><a href="#">Ensembles</a></p>
  <ul >
    <li><a href="#">GEFS</a></li>
    <li><a href="#">GEPS</a></li>
    <li><a href="#">EPS</a></li>
  </ul>
</div>
<div >
  <p><a href="#">Super Ensemble</a></p>
</div>

  • Related