Instantly share code, notes, and snippets.
Last active
June 22, 2018 14:45
-
Star
(1)
1
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save evanmwillhite/d14d310dc8d0edad53159d6ceea8948e to your computer and use it in GitHub Desktop.
Multi-level Responsive Menu with vanilla JS (IE10+). Children completely hidden on large screens.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function () { | |
'use strict'; | |
var mainMenuToggle = document.getElementById('main-menu-toggle'); | |
var mainMenu = document.getElementById('main-menu'); | |
var mainMenuList = document.getElementById('main-menu__list'); | |
var mainMenuClose = document.getElementById('main-menu__close'); | |
var mainMenuParent = document.querySelectorAll('.main-menu__item--with-child > a'); | |
var mainMenuBack = document.querySelectorAll('.main-menu__back'); | |
var resizeTimer; | |
// If Mobile, add active class (for transitions) | |
// Where el is the DOM element you'd like to test for visibility | |
function isHidden(el) { | |
return (el.offsetParent === null); | |
} | |
// Encapsulate resizing | |
function menuTransition() { | |
// Add and remove menu active (transition) class | |
if (!isHidden(mainMenuToggle)) { | |
mainMenu.classList.add('active'); | |
} | |
else { | |
mainMenu.classList.remove('active'); | |
} | |
} | |
// Run menuTransition() on resize | |
window.addEventListener('resize', function () { | |
// http://www.paulirish.com/2009/throttled-smartresize-jquery-event-handler/ | |
clearTimeout(resizeTimer); | |
resizeTimer = setTimeout(menuTransition, 250); | |
}); | |
// Run menuTransition() on load | |
menuTransition(); | |
// Menu Button Toggle | |
mainMenuToggle.addEventListener('click', function (e) { | |
mainMenu.classList.toggle('is-visible'); | |
e.preventDefault(); | |
}); | |
// Menu Close Toggle | |
mainMenuClose.addEventListener('click', function (e) { | |
mainMenu.classList.toggle('is-visible'); | |
mainMenuList.className = ('main-menu__list'); | |
e.preventDefault(); | |
}); | |
// Mobile Click Menu Transition | |
for (var i = 0; i < mainMenuParent.length; i++) { | |
mainMenuParent[i].addEventListener('click', function (e) { | |
var level = this.getAttribute('data-childLevel'); | |
var parent = this.parentNode; | |
// Make parent active | |
if (level === '1') { | |
var openItems = document.querySelectorAll('.open'); | |
if (openItems.length) { | |
openItems[0].classList.remove('open'); | |
} | |
if (parent.classList) { | |
parent.classList.add('open'); | |
} | |
else { | |
parent.className += ' ' + 'open'; | |
} | |
} | |
// Reset open items | |
mainMenuList.className = ('main-menu__list'); | |
// Add is-active-LEVEL class for each section | |
if (mainMenuList.classList) { | |
mainMenuList.classList.add('is-active-' + level); | |
} | |
else { | |
mainMenuList.className += ' ' + 'is-active-' + level; | |
} | |
e.preventDefault(); | |
}); | |
} | |
// Mobile Menu Back button | |
for (var r = 0; r < mainMenuBack.length; r++) { | |
mainMenuBack[r].addEventListener('click', function (e) { | |
var level = this.getAttribute('data-childLevel'); | |
mainMenuList.className = ('main-menu__list'); | |
// Add is-active-LEVEL class for each section | |
if (mainMenuList.classList) { | |
mainMenuList.classList.add('is-active-' + level); | |
} | |
else { | |
mainMenuList.className += ' ' + 'is-active-' + level; | |
} | |
e.preventDefault(); | |
}); | |
} | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$main-menu-bg: #f2f2f2; | |
// Mobile Menu Button | |
.main-menu-toggle { | |
// include SVG icon | |
border: none; | |
cursor: pointer; | |
display: inline-block; | |
font-size: 300%; | |
left: -2px; | |
outline: none; | |
padding: .7em; | |
position: absolute; | |
top: -60px; | |
z-index: 50; | |
&:hover { | |
opacity: .8; | |
} | |
@include large { | |
display: none; | |
} | |
} | |
// Menu styles | |
// Nav | |
.main-menu { | |
background-color: $main-menu-bg; | |
height: 100%; | |
left: 0; | |
overflow-y: auto; | |
position: fixed; | |
transform: translateX(-100%); | |
top:0; | |
width: 100%; | |
z-index: 100; | |
-webkit-overflow-scrolling: touch; | |
@include large { | |
background-color: transparent; | |
overflow: visible; | |
position: static; | |
transform: none; | |
} | |
&.active { | |
transition: all 0.5s; | |
@include large { | |
transition: none; | |
} | |
} | |
&.is-visible { | |
transform: translateX(0); | |
} | |
} | |
// UL | |
.main-menu__list { | |
height: 100%; | |
padding: 0; | |
margin: 0; | |
transition: all 0.35s; | |
// @TODO make this 3 level support infinite | |
&.is-active-1 { | |
transform: translateX(-100%); | |
@include medium { | |
transform: none; | |
} | |
.open { | |
height: 100%; | |
position: static; | |
z-index: 1; | |
ul { | |
display: block; | |
@include medium { | |
display: none; | |
} | |
} | |
} | |
.open a[data-childlevel="1"] + ul { | |
height: 100%; | |
& > li { | |
height: auto; | |
} | |
} | |
} | |
&.is-active-2 { | |
transform: translateX(-200%); | |
@include medium { | |
transform: none; | |
} | |
.open { | |
height: 100%; | |
position: static; | |
z-index: 1; | |
ul { | |
display: block; | |
@include medium { | |
display: none; | |
} | |
} | |
// iPhone 5S requires parent to be static even though child is fixed. | |
> ul > li { | |
position: static; | |
} | |
} | |
.open a[data-childlevel="2"] + ul { | |
height: 100%; | |
& > li { | |
height: auto; | |
} | |
} | |
} | |
} | |
// LI | |
.main-menu__item { | |
list-style: none; | |
@include large { | |
display: inline-block; | |
} | |
} | |
// Child Menu | |
.main-menu__list--child { | |
display: none; | |
left: 0; | |
position: fixed; | |
top: 0; | |
transform: translateX(100%); | |
width: 100%; | |
@include large { | |
display: none; | |
} | |
} | |
// LI with child | |
.main-menu__item--with-child { | |
> .main-menu__link { | |
&:after { | |
// include arrow icon for parent item | |
content: ""; | |
height: 25px; | |
position: absolute; | |
right: 16%; | |
top: 18%; | |
width: 25px; | |
@include small-med { | |
right: 12%; | |
} | |
@include small-large { | |
right: 9%; | |
} | |
} | |
@include large { | |
&:after { | |
display: none; | |
} | |
} | |
} | |
} | |
// A | |
.main-menu__link, | |
.main-menu__link:link, | |
.main-menu__link:visited, | |
.main-menu__back { | |
background-color: transparent; | |
border: none; | |
border-bottom: 1px solid $gray-lighter; | |
color: $red; | |
cursor: pointer; | |
display: block; | |
@include font-size(1.1); | |
font-weight: 600; | |
padding: 1em; | |
position: relative;; | |
text-align: left; | |
text-decoration: none; | |
width: 85%; | |
@include large { | |
border: none; | |
color: $gray-dark; | |
display: inline-block; | |
@include font-size(.9rem); | |
padding: 0 1em; | |
} | |
&:hover { | |
background-color: $red; | |
color: $white; | |
@include large { | |
background-color: transparent; | |
color: $red; | |
} | |
} | |
} | |
// Back button specific | |
.main-menu__back { | |
color: $gray-lighter; | |
&:before { | |
// include arrow icon for back | |
background-size: 10.2em 8.6em; | |
left: -.5em; | |
margin-right: 0; | |
top: 0.05em; | |
} | |
&:hover { | |
background-color: $white; | |
color: $gray-lighter; | |
} | |
} | |
// Mobile Menu Close Button | |
.main-menu__close { | |
background-color: $gray-dark; | |
border: none; | |
color: $white; | |
cursor: pointer; | |
font: 2em arial; | |
height: 100%; | |
position: absolute; | |
right: 0%; | |
top: 0; | |
width: 15%; | |
@include small-med { | |
width: 13%; | |
} | |
@include small-large { | |
width: 9%; | |
} | |
&::after { | |
// include close icon | |
content: ""; | |
font-size: 137%; | |
height: 1em; | |
left: 50%; | |
margin-left: -32px; | |
position: absolute; | |
top:.3em; | |
width: 1.1em; | |
} | |
&:hover { | |
background-color: $gray; | |
} | |
@include large { | |
display: none; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<button type="button" id="main-menu-toggle" class="main-menu-toggle"></button> | |
<nav id="main-menu" class="main-menu"> | |
<ul class="main-menu__list" id="main-menu__list"> | |
<li class="main-menu__item main-menu__item--with-child"> | |
<a href="{{ menu_link_url }}" class="main-menu__link" data-childLevel="1">{{ menu_link_text|default('First Item') }}</a> | |
<ul class="main-menu__list main-menu__list--child"> | |
<button class="main-menu__back" type="button" data-childLevel="0">Back</button> | |
<li class="main-menu__item main-menu__item--with-child"> | |
<a href="{{ menu_link_url }}" class="main-menu__link" data-childLevel="2">{{ menu_link_text|default('First Item - Child') }}</a> | |
<ul class="main-menu__list main-menu__list--child"> | |
<button class="main-menu__back" type="button" data-childLevel="1">Back</button> | |
<li class="main-menu__item"> | |
<a href="{{ menu_link_url }}" class="main-menu__link">{{ menu_link_text|default('First Item - 2nd Level') }}</a> | |
</li> | |
<li class="main-menu__item"> | |
<a href="{{ menu_link_url }}" class="main-menu__link">{{ menu_link_text|default('First Item - 2nd Level(2)') }}</a> | |
</li> | |
<li class="main-menu__item"> | |
<a href="{{ menu_link_url }}" class="main-menu__link">{{ menu_link_text|default('First Item - 2nd Level(3)') }}</a> | |
</li> | |
</ul> | |
</li> | |
<li class="main-menu__item"> | |
<a href="{{ menu_link_url }}" class="main-menu__link">{{ menu_link_text|default('First Item - Child(2)') }}</a> | |
</li> | |
<li class="main-menu__item"> | |
<a href="{{ menu_link_url }}" class="main-menu__link">{{ menu_link_text|default('First Item - Child(3)') }}</a> | |
</li> | |
</ul> | |
</li> | |
</ul> | |
<button type="button" id="main-menu__close" class="main-menu__close"></button> | |
</nav> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment