简体   繁体   English

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

[英]How to make drop down menus open properly?

I have 29 clickable drop down menus on a page.我在一个页面上有 29 个可点击的下拉菜单。 I need them to open when clicked on, then close when clicked on another one or anywhere else on the page.我需要它们在点击时打开,然后在点击另一个或页面上的任何其他地方时关闭。 I got this code from w3schools.com.我从 w3schools.com 得到了这个代码。

Right now this code will open the first menu on the page.现在此代码将打开页面上的第一个菜单。 When I click on another menu however, it will just open the first menu again.但是,当我单击另一个菜单时,它只会再次打开第一个菜单。 I cannot open any other menu on the page.我无法打开页面上的任何其他菜单。 The only menu that will open, no matter what one is clicked is the first menu.无论单击什么,唯一会打开的菜单是第一个菜单。

I played around with the code and was eventually able to get other menus to open, the problem was that the first menu would stay open.我玩弄代码并最终能够打开其他菜单,问题是第一个菜单将保持打开状态。 For example, I would click menu 1 and it would open.例如,我会单击菜单 1 并打开它。 When I clicked menu 2 it would open, but menu 1 would also remain open.当我单击菜单 2 时,它会打开,但菜单 1 也会保持打开状态。 This was for every menu on the page.这是页面上的每个菜单。

I messed with the code enough that no menus work anymore and I am having to resort to code I began with.我弄乱了代码,以至于菜单不再起作用,我不得不求助于我开始使用的代码。

 /* 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>

What I need is each menu to be able to be clicked on.我需要的是每个菜单都能被点击。 Then, when I click on the page, or another menu, the first menu will close and open up the second menu.然后,当我点击页面或其他菜单时,第一个菜单将关闭并打开第二个菜单。 Only one menu open at a time.一次只打开一个菜单。

Thank you!谢谢!

First, stay as far away as possible from W3Schools as it is widely known to have outdated, incomplete, or flat out wrong information. 首先,尽可能远离W3Schools,因为众所周知,它已经过时,不完整或者说是错误的信息。 As such, many of the techniques in your code are outdated and should not be used, such as onXyz inline event attributes , JavaScript event properties and the use of getElementsByClassName() . 因此,代码中的许多技术都已过时,不应使用,例如onXyz 内联事件属性 ,JavaScript事件属性和getElementsByClassName()的使用。 The Mozilla Developer's Network is well-known to be an authoritative resource. 众所周知, Mozilla Developer's Network是一个权威资源。

The overall solution here is to use event delegation and allow events to bubble up from their originating element (the event target ), to the document , where they are handled. 这里的整体解决方案是使用事件委托并允许事件从其原始元素( 事件目标冒泡到处理它们的document This allows us to create just one event handler instead of one for each menu. 这允许我们为每个菜单创建一个事件处理程序而不是一个。 Then, no matter which menu was clicked, the event handling function collapses them all and then just expands the one that was clicked. 然后,无论单击哪个菜单,事件处理函数都会将它们全部折叠,然后只展开所单击的菜单。

Note that in the following code, there is no JavaScript in the HTML, no elements have id attributes, and that the menus have a CSS class of hide applied to them by default instead of having display:none set in the element CSS class. 请注意,在以下代码中,HTML中没有JavaScript,没有元素具有id属性,并且菜单默认应用了一个CSS类hide ,而不是在元素CSS类中设置display:none This allows us to remove that class by itself that leave the rest of the CSS intact. 这允许我们自己删除该类,使其余的CSS保持不变。

There is no reliance on id s, so when you add another dropdown structure, it will just work with no changes to the JavaScript needed. 不依赖于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> 

Unfortunately I dont have enough reputation yet to comment on other peoples post. 不幸的是,我还没有足够的声誉来评论其他人的帖子。 I wanted to endorse the approach of @Scott Marcus who had posted before me. 我想赞同在我面前发布的@Scott Marcus的做法。 I have just a few parts which I would approach differently 我只有几个部分,我会采取不同的方式

// 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
        }
    });
})();

I put it all together here: https://codepen.io/anon/pen/dLJjrw 我把它放在一起: https//codepen.io/anon/pen/dLJjrw

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM