Home > other >  Why does adding a second dropdown mess up the JS to close menu when user clicks outside it?
Why does adding a second dropdown mess up the JS to close menu when user clicks outside it?

Time:12-13

I'm not super familiar with JS. I used the W3Schools tutorial for creating an on-click dropdown menu as a reference and added a second menu. However, only the second dropdown menu listed in the javascript maintains the functionality of closing when the user clicks outside the dropdown. (I can switch the order of the functions listed in the JS, and changing nothing else, that switches which menu has that close-when-click-outside functionality.)

Can anyone help me understand why that is? How to fix it would be a bonus but mostly I just don't get why it works for one menu and not the other.

/* When the user clicks on the button, 
toggle between hiding and showing the dropdown content */
function drop1() {
  document.getElementById("drop1").classList.toggle("show");
}

// Close the dropdown if the user clicks outside of it
window.onclick = function(e) {
  if (!e.target.matches('.dropbtn1')) {
    var drop1 = document.getElementById("drop1");
    if (drop1.classList.contains('show')) {
      drop1.classList.remove('show');
    }
  }
}

/* When the user clicks on the button, 
toggle between hiding and showing the dropdown content */
function drop2() {
  document.getElementById("drop2").classList.toggle("show");
}

// Close the dropdown if the user clicks outside of it
window.onclick = function(e) {
  if (!e.target.matches('.dropbtn2')) {
    var drop2 = document.getElementById("drop2");
    if (drop2.classList.contains('show')) {
      drop2.classList.remove('show');
    }
  }
}
.navbar {
  overflow: hidden;
  background-color: #333;
  font-family: Arial, Helvetica, sans-serif;
}

.navbar a {
  float: left;
  font-size: 16px;
  color: white;
  text-align: center;
  padding: 14px 16px;
  text-decoration: none;
}

.dropdown {
  float: left;
  overflow: hidden;
}

.dropbtn1,
.dropbtn2 {
  cursor: pointer;
  font-size: 16px;
  border: none;
  outline: none;
  color: white;
  padding: 14px 16px;
  background-color: inherit;
  font-family: inherit;
  margin: 0;
}

.navbar a:hover,
.dropdown:hover .dropbtn,
.dropbtn:focus {
  background-color: red;
}

.dropdown-content {
  display: none;
  position: absolute;
  background-color: #f9f9f9;
  min-width: 160px;
  box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
  z-index: 1;
}

.dropdown-content a {
  float: none;
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: block;
  text-align: left;
}

.dropdown-content a:hover {
  background-color: #ddd;
}

.show {
  display: block;
}
<div >
  <a href="#home">Home</a>
  <a href="#news">News</a>
  <div >
    <button  onclick="drop1()">Dropdown
    &nbsp;  
  </button>
    <div  id="drop1">
      <a href="#">Link 1</a>
      <a href="#">Link 2</a>
      <a href="#">Link 3</a>
    </div>
  </div>
  <div >
    <button  onclick="drop2()">Dropdown 2
    &nbsp;  
  </button>
    <div  id="drop2">
      <a href="#">Link 4</a>
      <a href="#">Link 5</a>
      <a href="#">Link 6</a>
    </div>
  </div>
</div>

<h3>Dropdown Menu inside a Navigation Bar</h3>
<p>Click on the "Dropdown" link to see the dropdown menu.</p>

Thank you!

CodePudding user response:

You are only allowed to have one onclick. The second will overwrite the first

Instead use eventListener and delegation

Notice I removed the inline click and I now only have one class instead of a class per button

window.addEventListener("load", function() {
  // click the dropdown if the user clicks outside it unless that is a button
  document.addEventListener("click", function(e) {
    const tgt1 = e.target.closest('.dropdown-content');
    const tgt2 = e.target.closest('.dropbtn');
    if (!tgt1 && !tgt2) {
      document.querySelectorAll('.dropdown-content').forEach(div => div.classList.remove('show'));
    }
  })
  document.querySelector(".navbar").addEventListener("click", function(e) {
    const tgt = e.target.closest("button");
    if (tgt && tgt.matches('.dropbtn')) {
      document.querySelectorAll('.dropdown-content').forEach(div => div.classList.remove('show'));
      document.getElementById(tgt.dataset.id).classList.add('show');
    }
  })
})
.navbar {
  overflow: hidden;
  background-color: #333;
  font-family: Arial, Helvetica, sans-serif;
}

.navbar a {
  float: left;
  font-size: 16px;
  color: white;
  text-align: center;
  padding: 14px 16px;
  text-decoration: none;
}

.dropdown {
  float: left;
  overflow: hidden;
}

.dropbtn {
  cursor: pointer;
  font-size: 16px;
  border: none;
  outline: none;
  color: white;
  padding: 14px 16px;
  background-color: inherit;
  font-family: inherit;
  margin: 0;
}

.navbar a:hover,
.dropdown:hover .dropbtn,
.dropbtn:focus {
  background-color: red;
}

.dropdown-content {
  display: none;
  position: absolute;
  background-color: #f9f9f9;
  min-width: 160px;
  box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
  z-index: 1;
}

.dropdown-content a {
  float: none;
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: block;
  text-align: left;
}

.dropdown-content a:hover {
  background-color: #ddd;
}

.show {
  display: block;
}
<div >
  <a href="#home">Home</a>
  <a href="#news">News</a>
  <div >
    <button  data-id="drop1">Dropdown
    &nbsp;  
  </button>
    <div  id="drop1">
      <a href="#">Link 1</a>
      <a href="#">Link 2</a>
      <a href="#">Link 3</a>
    </div>
  </div>
  <div >
    <button  data-id="drop2">Dropdown 2
    &nbsp;  
  </button>
    <div  id="drop2">
      <a href="#">Link 4</a>
      <a href="#">Link 5</a>
      <a href="#">Link 6</a>
    </div>
  </div>
</div>

<h3>Dropdown Menu inside a Navigation Bar</h3>
<p>Click on the "Dropdown" link to see the dropdown menu.</p>

CodePudding user response:

  • Related