如何使下拉菜单正确打开?

时间:2019-04-16 20:01:00

标签: javascript html drop-down-menu

我在页面上有29个可单击的下拉菜单。我需要它们在单击时打开,然后在单击页面上其他位置或其他位置时关闭。我从w3schools.com获得了此代码。

现在此代码将打开页面上的第一个菜单。但是,当我单击另一个菜单时,它将再次打开第一个菜单。我无法在页面上打开任何其他菜单。无论单击什么,唯一会打开的菜单是第一个菜单。

我玩弄了代码,最终能够打开其他菜单,问题是第一个菜单会保持打开状态。例如,我将单击菜单1,它将打开。当我单击菜单2时,它将打开,但是菜单1也将保持打开状态。这适用于页面上的每个菜单。

我把代码弄得一团糟,以至于没有菜单可用了,我不得不诉诸于我开始的代码。

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

// Close the dropdown menu if the user clicks outside of it
window.onclick = function(event) {
  if (!event.target.matches('.dropbtn')) {
    var dropdowns = document.getElementsByClassName("dropdown-content");
    var i;
    for (i = 0; i < dropdowns.length; i++) {
      var openDropdown = dropdowns[i];
      if (openDropdown.classList.contains('show')) {
        openDropdown.classList.remove('show');
      }
    }
  }
}
/* Dropdown Button */
.dropbtn {
  background-color: #3498DB;
  color: white;
  padding: 16px;
  font-size: 16px;
  border: none;
  cursor: pointer;
}

/* Dropdown button on hover & focus */
.dropbtn:hover, .dropbtn:focus {
  background-color: #2980B9;
}

/* The container <div> - needed to position the dropdown content */
.dropdown {
  position: relative;
  display: inline-block;
}

/* Dropdown Content (Hidden by Default) */
.dropdown-content {
  display: none;
  position: absolute;
  background-color: #f1f1f1;
  min-width: 160px;
  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
  z-index: 1;
}

/* Links inside the dropdown */
.dropdown-content a {
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: block;
}

/* Change color of dropdown links on hover */
.dropdown-content a:hover {background-color: #ddd}

/* Show the dropdown menu (use JS to add this class to the .dropdown-content container when the user clicks on the dropdown button) */
.show {display:block;}
<div class="dropdown">
  <button onclick="myFunction()" class="dropbtn">Dropdown</button>
  <div id="myDropdown" class="dropdown-content">
    <a href="#">Link 1</a>
    <a href="#">Link 2</a>
    <a href="#">Link 3</a>
  </div>
</div>

我需要的是每个菜单都可以单击。然后,当我单击页面或其他菜单时,第一个菜单将关闭并打开第二个菜单。一次仅打开一个菜单。

谢谢!

2 个答案:

答案 0 :(得分:0)

首先,应尽可能远离W3Schools,因为众所周知,W3Schools具有过时,不完整或平坦的错误信息。因此,您代码中的许多技术已过时,不应使用,例如onXyz inline event attributes,JavaScript事件属性和getElementsByClassName()的使用。众所周知,Mozilla Developer's Network是权威资源。

这里的整体解决方案是使用event delegation并允许事件从其原始元素(bubble)到documentevent target 。这使我们可以只创建一个事件处理程序,而不是为每个菜单创建一个。然后,无论单击哪个菜单,事件处理功能都会将其全部折叠,然后只展开被单击的菜单。

请注意,在以下代码中,HTML中没有JavaScript,没有元素具有id属性,并且菜单默认情况下对其应用了CSS类hide,而不是具有display:none在元素CSS类中设置。这样一来,我们就可以自行删除该类,而剩下的CSS则保持不变。

由于不依赖id,因此,当您添加另一个下拉结构时,它无需更改所需的JavaScript就可以使用。

// Get all the menus into an array, just once:
let menus = Array.prototype.slice.call(document.querySelectorAll(".dropdown-content"));

let openMenu = null;

/* When the user clicks on the button, 
toggle between hiding and showing the dropdown content */
function hideAllMenus() {
  // Get all the dropdowns into an array.
  menus.forEach(function(dropdown) {
    // If the element currently is not hidden
    if(!dropdown.classList.contains("hide")){
      openMenu = dropdown;
      dropdown.classList.add('hide'); // Hide it
    }
  });
}

// Close the dropdown menu if the user clicks outside of it
document.addEventListener("click", function(event) {

  hideAllMenus();      // Hide all the menus
  
  // If the clicked item was a menu
  if (event.target.classList.contains('dropbtn')) { 
    if(event.target.nextElementSibling === openMenu){
      event.target.nextElementSibling.classList.add("hide");
      openMenu = null;
    } else {
      // Go to the next element that is a sibling of the one that got clicked (the menu)
      // and toggle the use of the `hide` CSS class
      event.target.nextElementSibling.classList.remove("hide"); // Show the one that was clicked
      openMenu = event.target.nextElementSibling;
    }
  }
});
/* Dropdown Button */
.dropbtn {
  background-color: #3498DB;
  color: white;
  padding: 16px;
  font-size: 16px;
  border: none;
  cursor: pointer;
}

/* Dropdown button on hover & focus */
.dropbtn:hover, .dropbtn:focus {
  background-color: #2980B9;
}

/* The container <div> - needed to position the dropdown content */
.dropdown {
  position: relative;
  display: inline-block;
}

/* Dropdown Content (Hidden by Default) */
.dropdown-content {
  position: absolute;
  background-color: #f1f1f1;
  min-width: 160px;
  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
  z-index: 1;
}

/* Links inside the dropdown */
.dropdown-content a {
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: block;
}

/* Change color of dropdown links on hover */
.dropdown-content a:hover {background-color: #ddd}

/* Add or remove this to hide or show */
.hide {display:none;}
<div class="dropdown">
  <button class="dropbtn">Dropdown</button>
  <div class="dropdown-content hide">
    <a href="#">Link 1</a>
    <a href="#">Link 2</a>
    <a href="#">Link 3</a>
  </div>
</div>
<div class="dropdown">
  <button class="dropbtn">Dropdown</button>
  <div class="dropdown-content hide">
    <a href="#">Link 1</a>
    <a href="#">Link 2</a>
    <a href="#">Link 3</a>
  </div>
</div>
<div class="dropdown">
  <button class="dropbtn">Dropdown</button>
  <div class="dropdown-content hide">
    <a href="#">Link 1</a>
    <a href="#">Link 2</a>
    <a href="#">Link 3</a>
  </div>
</div>

答案 1 :(得分:0)

很遗憾,我没有足够的声誉来对其他人的帖子发表评论。我想赞同在我之前发布的@Scott Marcus的做法。我只有几个部分会有所不同

// I would use an IIFE to scope the variable "isOpen" that will be declared next
(function () {
    // I would declare a variable isOpen to determine whether any menu is open or not (we will use this later to check whether we run the function on document click or not to save unnecessary executions)
    var isOpen = false;
    function hideAllMenus() {
        Array.prototype.slice.call(document.querySelectorAll(".dropdown-content")).forEach(function(dropdown) {
           // we dont need to check here whether our dropdown has already the class "hide" or not. the engine takes care of this anyway. there won't be a second class added in case it already has one.
           dropdown.classList.add('hide');
        });
        isOpen = false; // set to false if the function ran
    }

    // close the dropdown menu if the user clicks outside of it
    document.addEventListener("click", function (event) {
        // only run this function if any menu is actually open
        if (isOpen) {
            hideAllMenus();      // Hide all the menus
        }

        // If the clicked item was a menu
        if (event.target.classList.contains('dropbtn')) { 
            event.target.nextElementSibling.classList.toggle("hide"); // Show the one that was clicked
            isOpen = true; // set to true because now one is open
        }
    });
})();

我在这里将它们放在一起:https://codepen.io/anon/pen/dLJjrw