Skip to content

Instantly share code, notes, and snippets.

@chrisdc
Last active November 2, 2018 19:49
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrisdc/4b09f80281217854622c to your computer and use it in GitHub Desktop.
Save chrisdc/4b09f80281217854622c to your computer and use it in GitHub Desktop.
Object Orientated navigation.js
( function() {
function Menu( containerId, options ) {
var container,
menu,
button;
function init() {
container = document.getElementById( containerId );
if ( ! container ) {
return;
}
menu = container.getElementsByTagName( 'ul' )[0];
button = container.getElementsByTagName( 'button' )[0];
// Hide menu toggle button if menu is empty and return early.
if ( 'undefined' === typeof menu ) {
if ( 'undefined' !== typeof button ) {
button.style.display = 'none';
}
return;
}
if ( -1 === menu.className.indexOf( 'nav-menu' ) ) {
menu.className += ' nav-menu';
}
// Call the methods specified in options.
if ( true === options.mobileToggle ) {
mobileToggle();
}
if ( true === options.a11yDropdowns ) {
a11yDropdowns();
}
}
/**
* mobileToggle
* Handles toggling the navigation menu for small screens.
*/
function mobileToggle() {
// Return early if the button does not exist.
if ( 'undefined' === typeof button ) {
return;
}
button.onclick = function() {
if ( -1 !== container.className.indexOf( 'toggled' ) ) {
container.className = container.className.replace( ' toggled', '' );
button.setAttribute( 'aria-expanded', 'false' );
menu.setAttribute( 'aria-expanded', 'false' );
} else {
container.className += ' toggled';
button.setAttribute( 'aria-expanded', 'true' );
menu.setAttribute( 'aria-expanded', 'true' );
}
};
}
/**
* a11yDropdowns
* Makes dropdown menus keyboard accessible. Also adds aria-haspoup on links with submenus.
*/
function a11yDropdowns() {
var links = menu.getElementsByTagName( 'a' ),
subMenus = menu.getElementsByTagName( 'ul' );
// Return early if there are no submenus.
if ( 0 === subMenus.length ) {
return;
}
// Set menu items with submenus to aria-haspopup="true"
for ( var i = 0, len = subMenus.length; i < len; i++ ) {
subMenus[i].parentNode.setAttribute( 'aria-haspopup', 'true' );
}
// Each time a menu link is focused or blurred call the function toggleFocus.
for ( i = 0, len = links.length; i < len; i++ ) {
links[i].onfocus = toggleFocus;
links[i].onblur = toggleFocus;
}
function toggleFocus() {
var current = this;
// Move up through the ancestors of the current link until we hit .nav-menu
// On li elements toggle the class .focus
while ( -1 === current.className.indexOf( 'nav-menu' ) ) {
if ( 'li' === current.tagName.toLowerCase() ) {
if ( -1 !== current.className.indexOf( 'focus' ) ) {
current.className = current.className.replace( ' focus', '' );
} else {
current.className += ' focus';
}
}
current = current.parentElement;
}
}
}
init();
}
// Create menu objects and call the appropriate methods.
new Menu( 'site-navigation', {
mobileToggle: true,
a11yDropdowns: true
} );
} )();
/**
* At the time of writing this is the basic css required to make dropdowns keyboard accessible in _s.
* As a general rule theme authors should add equivalent .focus selectors to any :hover selectors that target dropdowns.
*/
.main-navigation ul li.focus > ul {
left: auto;
}
.main-navigation ul ul li.focus > ul {
left: 100%;
}
@chrisdc
Copy link
Author

chrisdc commented Sep 30, 2014

If you would like to test this I've created a working prototype here feel free to comment if you have any thoughts or questions on specific parts of the code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment