Drop-down menus can be a great way to save screen real estate, but the standard drop-down has one major problem: it’s not accessible to people who use the keyboard to navigate.
Here’s how to fix that with jQuery.
First, the basic menu HTML:
<nav id="nav-main">
<ul>
<li><a href="javascript:;">Rubber Boots</a></li>
<li><a href="javascript:;">Unicycle Parts</a></li>
<li><a href="javascript:;">Dog Costumes</a>
<ul>
<li><a href="javascript:;">Pirate</a></li>
<li><a href="javascript:;">Clown</a></li>
<li><a href="javascript:;">Shark</a></li>
</ul>
</li>
<li><a href="javascript:;">Vintage Tubas</a></li>
<li><a href="javascript:;">Magical Items</a>
<ul>
<li><a href="javascript:;">Cloaks</a></li>
<li><a href="javascript:;">Wands</a></li>
<li><a href="javascript:;">Rings</a></li>
<li><a href="javascript:;">Glowing Orbs</a></li>
</ul>
</li>
<li><a href="javascript:;">Ostrich Feathers</a></li>
</ul>
</nav>
Next, some basic CSS:
nav ul,
nav li {
position: static;
left: 0;
list-style: none;
padding: 0;
margin: 0;
}
#nav-main {
float: right;
background-color: #EEE;
}
#nav-main li {
float: left;
position: relative;
}
#nav-main li li {
width: 100%;
border-top: 1px solid;
}
#nav-main li ul {
position: absolute;
top: 2.25em;
margin-left: 1.5em;
z-index: 1000;
width: 8em;
background-color: #EEE;
border: 1px solid;
border-top: none;
}
#nav-main a:link,
#nav-main a:visited {
display: block;
text-decoration: none;
color: #000;
padding: .75em;
}
#nav-main a:hover,
#nav-main a:focus {
background-color: #000;
color: #FFF;
}
The nested lists are absolutely positioned, relative to their parent list items. Now we move the nested lists offscreen with a left offset.
- A negative left offset value will send the nested lists to the left.
- A positive left offset value would send the nested lists to the right. We don’t want to do this, as it creates a wide page with horizontal scrolling.
#nav-main li ul {
blah blah blah
left: -999em;
}
We bring the nested lists back into place by changing their left offset to auto when the parent list item is hovered or has focus:
#nav-main li:hover ul,
#nav-main li:focus ul {
left: auto;
}
The Problem
However, this presents an accessibility problem. The browser knows when a list items is hovered, but list items are not natively focusable.
As this example shows, tabbing through the links by keyboard does not offer a good user experience.
The Solution
A little jQuery provides an elegant solution by adding a class name to the nested list when the parent li’s anchor has focus.
Add the script to your HTML:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
$(function() {
$('li a').focus( function () {
$(this).siblings('li ul').addClass('focused');
}).blur(function(){
$(this).siblings('li ul').removeClass('focused');
});
$('li a').focus( function () {
$(this).parents('li ul').addClass('focused');
}).blur(function(){
$(this).parents('li ul').removeClass('focused');
});
});
</script>
… and then change your li:focus selector from this:
#nav-main li:hover ul,
#nav-main li:focus ul {
left: auto;
}
… to this:
#nav-main li:hover ul,
#nav-main li ul.focused {
left: auto;
}
Now we have keyboard accessible drop-down navigation. Tab through the links to see it work.