Navigation / Hamburger
Description of component:
How to use this component
This component is a navigation element commonly used for the main navigation of a website.
This component has been coded to be reusabled, meaning there can be more than one on a page.
Note: This does come with the note that the nav's ID needs to be unique. It also means that functions hamburgerMenuClick() and menuTabbingFun() need to be called as many times as there are hamburger menus.
This component requires a aria-label=""
and should be a contextual nav name
Why list-style="";
? https://www.matuzo.at/blog/2023/removing-list-styles-without-affecting-semantics
Additional Features to improve UX
- When tabbing through the menu, once tabbing past the last link in the hamburger, it closes the hamburger automatically
Component Example
Additional copy to seperate menus: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer lectus nibh, maximus non velit vitae, laoreet fermentum sem. Phasellus sagittis pretium sodales. Curabitur tincidunt, urna venenatis sodales mollis, nisl nunc condimentum risus, et viverra tellus felis sed dui. Mauris a turpis molestie, vestibulum lorem vitae, lobortis tortor
Code Snippets
HTML
<!-- NOTE: data-nav="" on the button needs to be the ID of the aliging nav element -->
<button class="menu-toggle js--menu-toggle" id="menu-toggle" aria-label="hamburger menu label" aria-expanded="false" data-nav="menu">
<span aria-hidden="true">
<span></span><span></span><span></span>
</span>
</button>
<!-- NOTE: aria-labelledby="" on the nav element needs to be the ID of the aliging button element -->
<nav class="menu" aria-labelledby="menu-toggle" id="menu">
<ul>
<li>
<!-- NOTE: Each link needs to have a data-parentNav="" with the ID of the parent nav element -->
<a href="#" data-parentNav="menu">Home</a>
</li>
<li>
<a href="#" data-parentNav="menu">About</a>
</li>
<li>
<a href="#" data-parentNav="menu">Blog</a>
</li>
<li>
<a href="#" data-parentNav="menu">Contact</a>
</li>
</ul>
</nav>
CSS
.nav-hamburger {
ul {
list-style: "";
}
.menu-checkbox,
.menu {
display: none;
}
.menu-toggle {
&:focus {
outline: 2px solid;
}
>span {
display: block;
width: 3rem;
height: 2rem;
position: relative;
>span {
display: block;
width: 100%;
height: 5px;
background-color: black;
position: absolute;
transition: 0.35s ease all;
&:first-of-type {
top: 0;
left: 50%;
transform: translateX(-50%);
}
&:nth-of-type(2) {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
&:last-of-type {
top: 100%;
left: 50%;
transform: translate(-50%, -100%);
}
}
}
&[aria-expanded="true"] {
>span {
>span {
&:first-of-type, &:nth-of-type(2) {
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(45deg);
}
&:last-of-type {
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(-45deg);
}
}
}
}
}
.menu a {
display: block;
}
.menu-checkbox:checked + .menu {
display: block;
}
.menu.active {
display: block;
}
}
JS
/* Ensure the nav hamburger closes when the nav is not in focus, otherwise toggle the hamburger menu open/close
*
* menuToggle = the ID of the hamburger toggle button
* menu = the ID of the hamburger nav ele
* menuToggleLinks = grabbing all the hamburger nav ele's links
* menuLastLink = grabbing the last link in the hamburger nav ele
* navResetTriger = empty var that will switch to trigger the reset in the tabbing function
============================================= */
var navResetTrigger = '';
var hamburgerX = 0;
/* menuToggleFun()
* Check if the hamburger nav ele contains the class 'active'
* Toggle aria-expanded attr on hamburger button ele
* Toggle class 'active' on hamburger nav ele
*
* Pass through the hamburger button ID
============================================= */
function menuToggleFun(hamburgerToggleBtnId) {
var menuToggle = document.getElementById(hamburgerToggleBtnId);
var menu = document.getElementById(menuToggle.dataset.nav);
var menuOpen = menu.classList.contains("active");
var newMenuOpenStatus = !menuOpen;
menuToggle.setAttribute("aria-expanded", newMenuOpenStatus);
menu.classList.toggle("active");
}
/* menuTabbingFun()
* Check if the hamburger nav ele contains the class 'active'
* Toggle aria-expanded attr on hamburger nav ele
* Toggle class 'active' on hamburger nav ele
*
* Pass through the hamburger button ID
============================================= */
function menuTabbingFun(hamburgerToggleBtnId) {
var menuToggle = document.getElementById(hamburgerToggleBtnId);
var menu = document.getElementById(menuToggle.dataset.nav);
var menuToggleLinks = document.querySelectorAll('#' + menuToggle.dataset.nav + ' a');
var menuLastLink = menuToggleLinks[(menuToggleLinks.length - 1)];
// check if the activeElement is within a hamburger menu that is currently open
// if( document.getElementById(document.activeElement.dataset.parentNav) ) {
// console.log('????');
// if( document.getElementById(document.activeElement.dataset.parentNav).getAttribute('aria-expanded="true"') ) {
// hamburgerX = 1;
// console.log('what o.O');
// }
// }
// check if the activeElement is the last one in a hambuger menu
if( menu.contains(document.activeElement) ) {
navResetTrigger = '';
if( menuLastLink == document.activeElement) {
navResetTrigger = 'trigger';
hamburgerX = 1;
}
} else if ( !menu.contains(document.activeElement) && navResetTrigger == 'trigger' ){
// if hamburgerX is - trigger to close all hamburger menus and reset navResetTrigger
if( hamburgerX == 0 ) {
// close all hamburger navs after moving past the last link in tabbing order
document.querySelectorAll('.js--menu-toggle').forEach(function(m){
var nav = document.getElementById(m.dataset.nav);
m.setAttribute("aria-expanded", false);
nav.classList.remove("active");
});
navResetTrigger = '';
} else {
hamburgerX = 0;
}
}
}
/* hamburgerMenuClick()
*
* Hamburger Button OnClick()
* On click of the hamburger button trigger menuToggleFun
============================================= */
function hamburgerMenuClick(hamburgerToggleBtnId) {
document.getElementById(hamburgerToggleBtnId).addEventListener("click", function(event) {
menuToggleFun(hamburgerToggleBtnId);
});
}
// EDIT HERE: add a hamburgerMenuClick() for each hamburger menu and pass through the hamburger button id
hamburgerMenuClick('menu-toggle');
hamburgerMenuClick('menu-toggle2');
/* Window Keydown()
* In the tab check function, call menuTabbingFun()
============================================= */
window.addEventListener("keydown", function(e){
if (e.keyCode == 9 || e.keyCode == 40 || e.keyCode == 39 || e.keyCode == 38 || e.keyCode == 37) {
if (isTabbing !== null) {
window.clearTimeout(isTabbing);
}
var isTabbing = setTimeout(function() {
// EDIT HERE: add a menuTabbingFun() for each hamburger menu and pass through the hamburger button id
menuTabbingFun('menu-toggle');
menuTabbingFun('menu-toggle2');
});
}
});