A sliding multi-level navigation drawer menu written in vanilla javascript;
Use basic HTML, javascript, and CSS to create a tiered navigation suitable for narrow or wide screens
By Jim Keller, Eastern Standard
https://www.easternstandard.com
- Fully ADA / WCAG 2.0 / Section 508 compliant - supports keyboard navigation, tabbing, and aria labels
- Extremely customizable - see the options list below
- No dependencies
- ES6 compatible
- Useful for both simple or extremely deep menus
-
If using npm, simply run "npm install traversable_menu". Otherwise, add traversable_menu.css and traversable_menu.js to your project
-
Add the markup for your menu:
<div id="primary-menu" class="traversable-menu"> <div class="menu__panel"> <div class="menu__panel__title"><!-- automatically replaced with javascript --></div> <ul> <li class="menu__item"> <a href="#" class="menu__item__link">About Us</a> </li> <li class="menu__item"> <a href="#" class="menu__item__link">Academics</a> <a href="#" class="menu__panel__trigger--child">Explore ></a> <div class="menu__panel"> <div class="menu__panel__title"></div> <a href="#" class="menu__panel__trigger--top"></a> <a href="#" class="menu__panel__trigger--parent">Up a level (this gets replaced in JS)</a> <ul> <li class="menu__item"> <a href="#" class="menu__item__link">Sub item to Academics</a> </li> </ul> </div> </li> <li class="menu__item"><a href="#" class="menu__item__link">Another item with no children</a></li> </ul> </div> </div> - Initialize traversable menu using basic options:
<script> document.addEventListener("DOMContentLoaded", function() { let traversable = new TraversableMenu( { 'panel_title_first': 'Traversable Menu Example' // many additional options here; see below } ); traversable.initializeFromHTML("#primary-menu") }); </script> - You're done
Please note that options are nested the same way they are below, like so:
selectors: { //selector options here }, classes: { //classes options here }, triggers: { //trigger options here } ...etc selector for an individual menu panel - usually one level of the menu
default: .menu__panel
selector for the trigger element that goes one level deeper
default: .menu__panel__trigger--child
selector for the trigger element that moves one level up
default: .menu__panel__trigger--parent
selector for the trigger element that goes back to the top
default: .menu__panel__trigger--top
selector for the title for a panel
default: .menu__panel__title
selector for container that holds multiple panels. This should be your top-level selector.
default: .traversable-menu
selector for the currently active link, i.e. the page the user is on
default: .menu__item--active
selector for whether the current link is part of the active menu trail
default: .menu__item--active-trail
selector for an individual menu item (almost always an li tag)
default: .menu__item
selector for the actual menu item link (almost always an a tag)
default: .menu__item__link
Tabbing will be disabled on elements matching this selector when the menu panel is closed
default: a
class to set on top-level container after menu is initialized
default: traversable-menu--initialized
class that gets set on a panel when it is actively displayed
default: menu__panel--active
class that gets set on any panel that's in the active trail
default: menu__panel--active-trail
class that is set on any panel that is a parent of the current panel
default: menu__panel--child-open
class to set when a panel should be shown immediately, rather than animating in
default: -show-immediat
class to set on each panel that tells you the depth. [:n:] is replaced with the depth of the panel
default: menu__panel--depth-[:n:]
text to be set on the panel_trigger_parent. [:previous-title:] is automatically replaced with the parent menu's title
default: Up to [:previous-title:] menu
text to be set on the "up to top" trigger
type: string
default: Up to Main Menu
the depth at which to start showing 'up to main menu' link
type: integer
default: 2
whether to automatically remove 'up to main menu' link if the depth is less than triggers.top_depth
type: boolean
default: true
if panel_title_first is set, use that as our "top_text" at the first level below the topmost panel
type: boolean
default: false
the role attribute to set on the panel container
type: string
default: menubar
the role attribute to set on an individual panel
type: string
default: menu
the role attribute to set on a menu item
type: string
default: menuitem
set focus to first menu item link when panel is shown using keyboard
type: boolean
default: true
called before the panels are initialized. Passes the TraversableMenu object to the function as a parameter
type: function
default: none
called after the panels are initialized. Passes the TraversableMenu object to the function as a parameter
type: function
default: none
set to true to see debug messages
default: false
whether to automatically scroll to the top of the panel (so that the user doesn't land in the middle of a child panel)
type: boolean
default: true
hether to automatically determine the height of each individual panel
type: boolean
default: true
whether to automatically set the panel container height
type: boolean default: true
The duration of the "slide" animation in milliseconds. Should match your CSS.
type: integer
default: 350
the title of the first panel
type: string
default: none
title of subsequent panels. [:menu-title:] will be replaced by the text of the link that expands to show the menu
type: string
default: [:menu-title:]
whether to automatically traverse (on initialize) to the menu item identified by the menu_item_active selector above
type: boolean
default: true
by default, auto traverse shows siblings of the current page. Increase this number to have it automatically traverse deeper
type: integer default: 0
hide errors if no panel container is found
type: boolean
default: true
The below is just for a visual guide as to how the options are nested. The values here are just for example purposes, you almost certainly don't want to use them.
<script> document.addEventListener("DOMContentLoaded", function() { var traversable = new TraversableMenu( { selectors: { 'panel': '.my-menu-panel-selector', 'panels_container': '#my-menu-panels' }, classes: { 'panel_active': '.active' }, triggers: { 'parent_text': 'Click to see the [:previous-title] menu' }, accessibility: { 'container_role': 'menubar' }, callbacks: { panels { initialize { after: function( traversable_menu_obj ) { //perhaps do something here after the panels are initialized } } } } 'panel_title_first': 'Traversable Menu Example' // many additional options here; see below } ); }); </script> 