In the code below for a simple menu, if the sub-menu is expanded/showing, then I need the sub-menu to collapse on several conditions:
- When the main menu or sub-menu LI is clicked
- When the viewport is larger than the defined minimum width
- When a click happens anywhere else on the screen
Number 2 works and number 1 works as long as I don't declare the addEventListener for the mainMenuID element. For some reason, when I add that event listener for any mainMenuID element, it takes precedence over anything else thus clicking on a main menu item other than Portfolio or its sub-menu items, expands and collapses the menu, but clicking on the submenuID or the ulID does nothing. As soon as I remove the eventlistener for mainMenuID, clicking on submenuID or ulID expand/collapse the sub-menu.
The second question is, how do I add a click event listener to the rest of the page so that if the sub-menu is showing, the click collapses the sub-menu.
Thank you!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>
<style media="screen">
body{
background-color: black;
}
nav ul {
position: relative;
text-align: left;
}
nav ul li {
display: block; /* WHAT???? REMOVING THIS ACTUALLY MAKES TEXT-DECORATIONS REAPPEAR.... WHY???*/
}
nav a {
text-decoration: none;
}
/* Hide dropdowns by default */
nav ul ul {
position: relative;
display: none;
left: 0;
}
/* Display Dropdowns on Click */
nav ul li.showSubmenu > ul {
display: block;
position: relative;
width: 30%;
}
.menu a {
text-decoration-style: none;
text-decoration: none;
background-color: black;
color: white;
}
.menu a:hover{
background-color: white;
color: black;
}
</style>
<body>
<nav id="mainMenuID" class="menu">
<ul>
<li class="menuItem"><a href="#">Home</a></li>
<li class="menuItem"><a href="#">About</a></li>
<li id="submenuItem" class="submenu_class"><a href="#"><span id="submenuID">Portfolio ▼</span></a>
<ul id="ulID">
<li><a href="#">Landscape</a></li>
<li><a href="#">Architecture</a></li>
<li><a href="#">Animal</a></li>
<li><a href="#">Other</a></li>
</ul>
</li>
<li class="menuItem"><a href="#">Information</a></li>
<li class="menuItem"><a href="#">Contact</a></li>
</ul>
</nav>
<script>
function openSubmenu() {
if (document.getElementsByClassName('showSubmenu').length == 0){
document.getElementById("submenuItem").classList.toggle("showSubmenu");
document.getElementById("submenuID").textContent = "Portfolio ▲";
} else {
document.getElementById("submenuItem").classList.toggle("showSubmenu");
document.getElementById("submenuID").textContent = "Portfolio ▼";
}
}
function resetSubmenu() {
var submenuElements = document.getElementsByClassName('submenu_class showSubmenu');
for (var i = 0; i < submenuElements.length; i++) {
submenuElements[i].setAttribute('class', 'submenu_class');
document.getElementById("submenuID").textContent = "Portfolio ►";
}
}
function screenWidthFunction(x) {
if (x.matches) {//if it's a narrow screen
document.getElementById("submenuID").textContent = "Portfolio ▼";
document.getElementById("ulID").addEventListener("click", openSubmenu, true);
document.getElementById("submenuID").addEventListener("click", openSubmenu, true);
document.getElementById("mainMenuID").addEventListener("click", openSubmenu, true);
} else {
resetSubmenu();
document.getElementById("submenuID").textContent = "Portfolio ►";
document.getElementById("ulID").removeEventListener("click", openSubmenu);
document.getElementById("submenuID").removeEventListener("click", openSubmenu);
document.getElementById("mainMenuID").removeEventListener("click", openSubmenu);
}
}
var x = window.matchMedia("(max-width: 480px)");
screenWidthFunction(x);
x.addListener(screenWidthFunction);
</script>
<> Hi Robidu, I had this menu working using just CSS, but I was led to use JS for some of its functionality, which stopped most of the CSS from working. So at glacier speeds, I’ve been trying to learn JS on the fly. Believe it or not, learning from videos, W3Schools, etc…. I’ve been writing, re-working, and kludging this menu into existence for the better part of 5 or 6 weeks. Totally ridiculous. It seems you understand and are readily familiar with JS. It would take me many more weeks of trying to figure out how to implement all of your advice, and along the way, I would very likely destroy what already works. Is there any way I could kindly ask you to please save me that pain by providing me with either the code snippets for where they need to go, or just more specific advice. I’ve excerpted below the parts of your text about which I have questions (because of the character limits on comments, I have to split them across multiple comments):
- “…you should use CSS to hide the submenu since I think that this is to be the default.” q. Yes, it is the default. I thought I was already doing this in “nav ul li.showSubmenu > ul {…}” in my CSS.
- “…implement an initialization stage that fires on DOMContentReady to set up a support infrastructure.” q. This, I would never know how to figure out myself.
- “…need a flag that indicates whether or not the menu is open so that the handler can react accordingly…spare yourself the problem with "toggle"…have the menu's state at hand…” q. I thought the “toggling” of the class “showSubmenu” was serving as that kind of ‘flag’. How and where would I use the variable in place of the toggle?
- “…spare yourself…repeatedly attaching and removing event listeners.”, and later on “If the menu is hidden, it doesn't get any events at all.” q. Are you saying to assign the listeners to variables anywhere in the JS code? Would I not still need to refer to those variables in the same places where I use the listeners now and in the same manner?
- “…attach an event listener that checks for mouse events to the the item that opens the submenu here.” q. You mean other than the listener I have on id=“submenuID”? What would be different about what you are advising?
- “…attach a resize event handler to the window that performs this check...” q. I cropped the screen width code from W3’s examples. How/where would using innerWidth/innerHeight be different?
- “…set the bubbling stage to true to intercept the event”. q. I haven’t been able to find any explanation about bubbling works if you apply one kind to the items in a menu system, and another kind to other elements on a page, etc. I understand the basics, but couldn’t figure out why it wasn’t working on this simple menu when all three listeners were used.
“Collapsing the menu when a menu item is clicked…attach an event handler to each individual menu item (the best spot would be the links inside the menu”. q. Would I have to assign an ID to each LI?
Overall question: I used a NAV element with ULs and LIs because I was told it’s the most accessible for screen readers. Is there a better way to do what I’m trying to do with this menu or am I sort of on the right track?
I sincerely apologize for all the follow-up questions/clarifications, but I’m sincerely spinning my wheels on this, going in circles, in rough seas, wearing a blindfold…. and any other analogy you can throw into the mix.
I would truly appreciate any specific coding you can provide to demonstrate the specifics of your advice. (or even point me to examples so that I can try and figure them out).