<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!------ Include the above in your HEAD tag ---------->
<div class="nav-wrapper">
<a href="#" class="js-nav-toggle">
<span></span>
</a>
<nav role="navigation">
<div class="nav-toggle">
<span class="nav-back"></span>
<span class="nav-title">Menu</span>
<span class="nav-close"></span>
</div>
<ul>
<li class="has-dropdown">
<a href="#">Category</a>
<ul>
<li><a href="#">Cat 1</a></li>
<li><a href="#">Cat 2</a></li>
<li><a href="#">Cat 3</a></li>
<li><a href="#">Cat 4</a></li>
<li><a href="#">Cat 5</a></li>
<li><a href="#">Cat 6</a></li>
<li><a href="#">Cat 7</a></li>
</ul>
</li>
<li class="has-dropdown">
<a href="#">Apparel</a>
<ul>
<li class="has-dropdown">
<a href="#">Loremus</a>
<ul>
<li><a href="#">Item 1</a></li>
<li><a href="#">Item 2</a></li>
<li><a href="#">Item 3</a></li>
</ul>
</li>
<li><a href="#">Aliquam</a></li>
<li><a href="#">Vestibulum</a></li>
</ul>
</li>
<li>
<a href="#">Clients</a>
</li>
<li class="has-dropdown">
<a href="#">Contact Us</a>
<ul>
<li class="has-dropdown">
<a href="#">By mail</a>
<ul>
<li><a href="#">mail 1</a></li>
<li><a href="#">mail 2</a></li>
<li><a href="#">mail 3</a></li>
</ul>
</li>
<li><a href="#">Anything</a></li>
<li><a href="#">Tester</a></li>
</ul>
</li>
</ul>
</nav>
</div>
.debugger {
position: absolute;
left: 50%;
top: 0;
}
* {
margin: 0;
box-sizing: border-box;
font-family: "Open Sans", Arial, sans-serif;
}
.nav-wrapper {
position: relative;
width: 300px;
height: 100vh;
transition: transform 0.3s;
transform: translateX(-100%);
}
.nav-wrapper.show-menu {
transform: none;
}
.js-nav-toggle {
position: absolute;
top: 0;
right: -60px;
width: 43px;
height: 40px;
margin: 15px 0 0 15px;
display: block;
float: left;
padding: 0;
color: #345;
border: 2px solid #345;
z-index: 2;
}
.js-nav-toggle span {
position: relative;
background-color: #345;
height: 2px;
display: block;
width: 22px;
margin: 17px auto 0;
transition: all 0.4s;
transition-delay: 0.3s;
}
.js-nav-toggle span:before, .js-nav-toggle span:after {
content: '';
position: absolute;
display: block;
width: 20px;
height: 0;
left: 1px;
top: 50%;
margin-top: -7px;
transition: all 0.3s 0.3s;
}
.js-nav-toggle span:before {
box-shadow: 0 14px 0 1px #345;
}
.js-nav-toggle span:after {
box-shadow: 0 0 0 1px #345;
}
.show-menu .js-nav-toggle span {
background-color: transparent;
}
.show-menu .js-nav-toggle span:before {
transform: rotate(-45deg);
}
.show-menu .js-nav-toggle span:after {
transform: rotate(45deg);
}
.show-menu .js-nav-toggle span:before, .show-menu .js-nav-toggle span:after {
margin-top: 0;
box-shadow: 0 0 0 1px #345;
}
nav {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
overflow-x: hidden;
overflow-y: auto;
box-shadow: 0 0 5px 1px #ddd;
background-color: #fafafa;
}
nav .nav-toggle {
position: absolute;
top: 0;
left: 0;
width: 100%;
padding: 0.45em 0.6em;
background-color: #456;
color: #fff;
z-index: 100;
cursor: pointer;
transition: backgroun-color 0.2s;
}
nav .nav-toggle:hover {
background-color: #345;
}
nav .nav-toggle.back-visible .nav-back {
opacity: 1;
}
nav .nav-toggle.back-visible .nav-title {
transform: translateX(40px);
}
nav .nav-title {
position: absolute;
left: 0;
top: 0.8em;
padding-left: 0.7em;
transition: transform 0.3s;
}
nav .nav-back {
display: inline-block;
position: relative;
width: 30px;
height: 30px;
vertical-align: middle;
z-index: 1;
opacity: 0;
transition: opacity 0.2s;
}
nav .nav-back:before, nav .nav-back:after {
content: '';
position: absolute;
top: 50%;
}
nav .nav-back:before {
left: 50%;
width: 9px;
height: 9px;
border: 2px solid currentcolor;
border-right-color: transparent;
border-bottom-color: transparent;
transform: translate(-50%, -50%) rotateZ(-45deg);
}
nav .nav-back:after {
left: 28%;
width: 15px;
height: 2px;
background-color: currentcolor;
margin-top: -1px;
}
nav a {
display: block;
position: relative;
padding: 0.7em;
border-bottom: 1px solid #eee;
color: #999;
text-decoration: none;
transition: color 0.15s, background-color 0.15s;
}
nav a:hover {
color: #333;
background-color: #efefef;
}
nav ul {
list-style: none;
padding: 45px 0 0;
transition: transform 0.3s;
background-color: #fafafa;
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
}
nav ul ul {
display: none;
left: 100%;
}
nav li.has-dropdown > a {
padding-right: 2.5em;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
nav li.has-dropdown > a:after {
content: '';
position: absolute;
top: 50%;
right: 1em;
width: 9px;
height: 9px;
border: 1px solid currentcolor;
border-left-color: transparent;
border-top-color: transparent;
transform: translateY(-90%) rotateZ(-45deg);
transition: transform 0.3s;
transform-origin: 100%;
}
nav li.nav-dropdown-open ul {
display: block;
}
(function($) {
this.MobileNav = function() {
this.curItem,
this.curLevel = 0,
this.transitionEnd = _getTransitionEndEventName();
var defaults = {
initElem: ".main-menu",
menuTitle: "Menu"
}
// Check if MobileNav was initialized with some options and assign them to the "defaults"
if (arguments[0] && typeof arguments[0] === "object") {
this.options = extendDefaults(defaults, arguments[0]);
}
// Add to the "defaults" ONLY if the key is already in the "defaults"
function extendDefaults(source, extender) {
for (option in extender) {
if (source.hasOwnProperty(option)) {
source[option] = extender[option];
}
}
}
MobileNav.prototype.getCurrentItem = function() {
return this.curItem;
};
MobileNav.prototype.setMenuTitle = function(title) {
defaults.menuTitle = title;
_updateMenuTitle(this);
return title;
};
// Init is an anonymous IIFE
(function(MobileNav) {
var initElem = ($(defaults.initElem).length) ? $(defaults.initElem) : false;
if (initElem) {
defaults.initElem = initElem;
_clickHandlers(MobileNav);
_updateMenuTitle(MobileNav);
} else {
console.log(defaults.initElem + " element doesn't exist, menu not initialized.");
}
}(this));
function _getTransitionEndEventName() {
var i,
undefined,
el = document.createElement('div'),
transitions = {
'transition': 'transitionend',
'OTransition': 'otransitionend', // oTransitionEnd in very old Opera
'MozTransition': 'transitionend',
'WebkitTransition': 'webkitTransitionEnd'
};
for (i in transitions) {
if (transitions.hasOwnProperty(i) && el.style[i] !== undefined) {
return transitions[i];
}
}
};
function _clickHandlers(menu) {
defaults.initElem.on('click', '.has-dropdown > a', function(e) {
e.preventDefault();
menu.curItem = $(this).parent();
_updateActiveMenu(menu);
});
defaults.initElem.on('click', '.nav-toggle', function() {
_updateActiveMenu(menu, 'back');
});
};
// TODO: Make this DRY (deal with waiting for transitionend event)
function _updateActiveMenu(menu, direction) {
_slideMenu(menu, direction);
if (direction === "back") {
/*defaults.initElem.children('ul').one(menu.transitionEnd, function(e) {
menu.curItem.removeClass('nav-dropdown-open nav-dropdown-active');
menu.curItem = menu.curItem.parent().closest('li');
menu.curItem.addClass('nav-dropdown-open nav-dropdown-active');
_updateMenuTitle(menu);
});*/
menu.curItem.removeClass('nav-dropdown-open nav-dropdown-active');
menu.curItem = menu.curItem.parent().closest('li');
menu.curItem.addClass('nav-dropdown-open nav-dropdown-active');
_updateMenuTitle(menu);
} else {
menu.curItem.addClass('nav-dropdown-open nav-dropdown-active');
_updateMenuTitle(menu);
}
};
// Update main menu title to be the text of the clicked menu item
function _updateMenuTitle(menu) {
var title = defaults.menuTitle;
if (menu.curLevel > 0) {
title = menu.curItem.children('a').text();
defaults.initElem.find('.nav-toggle').addClass('back-visible');
} else {
defaults.initElem.find('.nav-toggle').removeClass('back-visible');
}
$('.nav-title').text(title);
};
// Slide the main menu based on current menu depth
function _slideMenu(menu, direction) {
if (direction === "back") {
menu.curLevel = (menu.curLevel > 0) ? menu.curLevel - 1 : 0;
} else {
menu.curLevel += 1;
}
defaults.initElem.children('ul').css({
"transform": "translateX(-" + (menu.curLevel * 100) + "%)"
});
};
}
}(jQuery));
$(document).ready(function() {
var MobileMenu = new MobileNav({
initElem: "nav",
menuTitle: "Push menu demo",
});
$('.js-nav-toggle').on('click', function(e) {
e.preventDefault();
$('.nav-wrapper').toggleClass('show-menu');
});
});