简体   繁体   中英

How to make drop down menus open properly?

I have 29 clickable drop down menus on a page. 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.

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. When I clicked menu 2 it would open, but menu 1 would also remain open. 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. 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() . The Mozilla Developer's Network is well-known to be an authoritative resource.

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. 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. This allows us to remove that class by itself that leave the rest of the CSS intact.

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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