I am just learning Javascript (very beginning) and I am having an issue when trying to make a hidden "sub navigation" menu appear when focus is applied to an element. I am keeping it very basic (I think).
The idea is the same as displaying a hidden "sub navigation" menu on hover. When user tabs to element and focus is applied the menu shows and the links in the menu become focusable. When the user tabs off of the menu, the menu hides itself again. I want this happen on any number of navigation elements.
The HTML:
<nav role="navigation">
<ul>
<li><a href="#">Link 1</a></li>
<li><a href="#" class="has-dropdown">Link 2</a>
<ul class="dropdown">
<li><a href="#">Link 2a</a></li>
<li><a href="#">Link 2b longer</a></li>
<li><a href="#">Link 2c longest text</a></li>
</ul>
</li>
<li><a href="#">Link 3</a></li>
<li><a href="#">Link 4</a></li>
</ul>
</nav>
Not sure you need it but here is the CSS:
nav[role=navigation] {
width: 100%;
background: #505050;
font-family: Oswald,sans-serif;
font-weight: 200;
}
nav[role=navigation] ul {
margin: 0;
padding: 0;
list-style: none;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
}
nav[role=navigation] ul li {
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
text-align: center;
padding: .8333rem;
border-right: 1px solid #252525;
position: relative;
}
nav[role=navigation] ul li:last-of-type {
border-right: none;
}
nav[role=navigation] ul li:hover a + ul {
display: block;
}
nav[role=navigation] ul ul {
list-style: none;
position: absolute;
top: 100%;
left: 0;
background: #252525;
padding: 0;
width: 200%;
text-align: left;
display: none;
}
nav[role=navigation] ul ul.active {
display: block;
}
nav[role=navigation] ul a {
padding: 0 1rem;
color: #fff;
text-decoration: none;
}
nav[role=navigation] ul a:focus,
nav[role=navigation] ul a:hover {
text-decoration: underline;
color: #ccc;
}
The Javascript:
var topLevel = document.getElementsByClassName("has-dropdown");
for (var i = 0; i < topLevel.length; i++) {
topLevel[i].onfocus = function() {
console.log("you have activated the dropdown");
topLevel.nextElementSibling.className = "active";
}
}
When I tab to the link which triggers this, I get the log message correctly on any link which has a sub menu but the sub menu does not display. When I run from the console I get the error:
"Cannot set property "className" of undefined."
I believe this has something to do with the variable being an array because if I set the index of topLevel such as topLevel[0] it works perfectly.
I want to use vanilla JS. No JQuery.
I have searched for similar questions but cant find any answers which are over my head or only give a JQuery solution.
I've looked at:
Drop down menu to hide on click event
For-each over an array in JavaScript? --> This one made my brain bleed.
Target items in an array on click?
How to split an array with retrieved values from DOM
The answer may be hiding in one of the above posts but if it is, I can't find it.