Skip to content

Instantly share code, notes, and snippets.

@kerelist
Last active June 20, 2017 21:56
Show Gist options
  • Save kerelist/b3f2b55c63f2f0d1e8368b5548a123b9 to your computer and use it in GitHub Desktop.
Save kerelist/b3f2b55c63f2f0d1e8368b5548a123b9 to your computer and use it in GitHub Desktop.
A vanilla js configurable reusable snippet to add tab functionality to two-level dropdown navigation elements.
/**
* const globals
* = general helper functions
**/
const globals = {
//from https://gomakethings.com/climbing-up-and-down-the-dom-tree-with-vanilla-javascript/
getClosest: function ( elem, selector ) {
// Element.matches() polyfill
if (!Element.prototype.matches) {
Element.prototype.matches =
Element.prototype.matchesSelector ||
Element.prototype.mozMatchesSelector ||
Element.prototype.msMatchesSelector ||
Element.prototype.oMatchesSelector ||
Element.prototype.webkitMatchesSelector ||
function(s) {
var matches = (this.document || this.ownerDocument).querySelectorAll(s),
i = matches.length;
while (--i >= 0 && matches.item(i) !== this) {}
return i > -1;
};
}
// Get closest match
for ( ; elem && elem !== document; elem = elem.parentNode ) {
if ( elem.matches( selector ) ) return elem;
}
return null;
},
//from https://stackoverflow.com/a/842346
getChildren: function(n, skipMe){
var r = [];
for ( ; n; n = n.nextSibling )
if ( n.nodeType == 1 && n != skipMe)
r.push( n );
return r;
},
//from https://stackoverflow.com/a/842346
getSiblings: function(n) {
return getChildren(n.parentNode.firstChild, n);
}
}
/**
* CONFIG BLOCK
**/
//primary menu ID selector
const primaryMenuSelector = '#primary-navigation';
//class of <li> elements that are parents of submenus
const subNavIndicator = '.has-children';
//all menu links
const allPrimaryLinks = document.querySelectorAll('#primary-navigation a');
//all focusable elements on the page
const allFocus = document.querySelectorAll('a, button, input');
//all elements that could be given a class of "focus" by addTabHandler
const allMenuLis = document.querySelectorAll('#primary-navigation li');
/**
* addTabHandler
* Listen for and handle focus on primary menu <a>'s
* @private
* @param {Array} array Array of all primary menu links
* @param {String} parentSelector Selector of first-level li with a submenu (i.e. '.has-children')
*/
var addTabHandler = function(array, parentSelector) {
for (var i=0; i < array.length; i++) {
array[i].addEventListener("focus", function() {
//check if there is a parent with the class "has-children"
var hasDropDown = globals.getClosest(this.parentNode, parentSelector);
//if exists, add 'focus' to current parent with children
if (hasDropDown != null || hasDropDown != undefined) {
hasDropDown.classList.add('focus');
}
//remove "focus" from all other dropdowns not currently in focus
var allSibDropdowns = globals.getSiblings(this.parentNode);
for (var g = 0; g < allSibDropdowns.length; g++) {
allSibDropdowns[g].classList.remove('focus');
}
});
}
};
/**
* removeFocusStatesOnNav
* Listen for all focus states and remove 'focus' class on nav elements if currently focused element is not navigation
* @private
* @param {String} navParentSelector ID of primary navigation parent element
* @param {Array} focusClassList Array containing all of the <li> elements in the main nav
* @param {Array} allFocusElements Array containing all of the focusable elements on the page (or the one right after the main nav if you know it)
*/
//remove focus class from all menu items when not focused on a menu element
var removeFocusStatesOnNav = function(navParentSelector, focusClassList, allFocusElements) {
for (var j=0; j < allFocusElements.length; j++) {
allFocusElements[j].addEventListener("focus", function() {
var isMainMenu = (globals.getClosest(this, navParentSelector) != null || globals.getClosest(this, navParentSelector) != undefined) ? true : false;
if (!isMainMenu) {
for (var y=0; y < focusClassList.length; y++) {
focusClassList[y].classList.remove('focus');
}
}
});
}
};
addTabHandler(allPrimaryLinks, subNavIndicator);
removeFocusStatesOnNav(primaryMenuSelector, allMenuLis, allFocus);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment