#Html/Css/Js - 13 Oct 2020
Merhabalar, Codrops'ta denk gelmiş olduğum ve hoşuma giden bir multi menüyü sizlerle paylaşmak istiyorum. Kullanımı oldukça kolay ve multi level bir menü. Geçiş efektleriyle görsel olarak desteklenmiş ve tamamen mobil uyumlu. Moderns css yöntemleri kullanıldığı için güncel tarayıcılarda hatasız olarak çalışmaktadır. Eski sürüm tarayıcılarda görsel olarak hata alabilirsiniz.
Html Kodları
<!-- Main container -->
<div class="container">
<!-- Menu toggle for mobile version -->
<button class="action action--open" aria-label="Open Menu"><span class="icon icon--menu"></span></button>
<!-- Menu -->
<nav id="ml-menu" class="menu">
<!-- Close button for mobile version -->
<button class="action action--close" aria-label="Close Menu"><span class="icon icon--cross"></span></button>
<div class="menu__wrap">
<ul data-menu="main" class="menu__level">
<li class="menu__item"><a class="menu__link" data-submenu="submenu-1" href="#">Vegetables</a></li>
<li class="menu__item"><a class="menu__link" data-submenu="submenu-2" href="#">Fruits</a></li>
<li class="menu__item"><a class="menu__link" data-submenu="submenu-3" href="#">Grains</a></li>
<li class="menu__item"><a class="menu__link" data-submenu="submenu-4" href="#">Mylk & Drinks</a></li>
</ul>
<!-- Submenu 1 -->
<ul data-menu="submenu-1" class="menu__level">
<li class="menu__item"><a class="menu__link" href="#">Stalk Vegetables</a></li>
<li class="menu__item"><a class="menu__link" href="#">Roots & Seeds</a></li>
<li class="menu__item"><a class="menu__link" href="#">Cabbages</a></li>
<li class="menu__item"><a class="menu__link" href="#">Salad Greens</a></li>
<li class="menu__item"><a class="menu__link" href="#">Mushrooms</a></li>
<li class="menu__item"><a class="menu__link" data-submenu="submenu-1-1" href="#">Sale %</a></li>
</ul>
<!-- Submenu 1-1 -->
<ul data-menu="submenu-1-1" class="menu__level">
<li class="menu__item"><a class="menu__link" href="#">Fair Trade Roots</a></li>
<li class="menu__item"><a class="menu__link" href="#">Dried Veggies</a></li>
<li class="menu__item"><a class="menu__link" href="#">Our Brand</a></li>
<li class="menu__item"><a class="menu__link" href="#">Homemade</a></li>
</ul>
<!-- Submenu 2 -->
<ul data-menu="submenu-2" class="menu__level">
<li class="menu__item"><a class="menu__link" href="#">Citrus Fruits</a></li>
<li class="menu__item"><a class="menu__link" href="#">Berries</a></li>
<li class="menu__item"><a class="menu__link" data-submenu="submenu-2-1" href="#">Special Selection</a></li>
<li class="menu__item"><a class="menu__link" href="#">Tropical Fruits</a></li>
<li class="menu__item"><a class="menu__link" href="#">Melons</a></li>
</ul>
<!-- Submenu 2-1 -->
<ul data-menu="submenu-2-1" class="menu__level">
<li class="menu__item"><a class="menu__link" href="#">Exotic Mixes</a></li>
<li class="menu__item"><a class="menu__link" href="#">Wild Pick</a></li>
<li class="menu__item"><a class="menu__link" href="#">Vitamin Boosters</a></li>
</ul>
<!-- Submenu 3 -->
<ul data-menu="submenu-3" class="menu__level">
<li class="menu__item"><a class="menu__link" href="#">Buckwheat</a></li>
<li class="menu__item"><a class="menu__link" href="#">Millet</a></li>
<li class="menu__item"><a class="menu__link" href="#">Quinoa</a></li>
<li class="menu__item"><a class="menu__link" href="#">Wild Rice</a></li>
<li class="menu__item"><a class="menu__link" href="#">Durum Wheat</a></li>
<li class="menu__item"><a class="menu__link" data-submenu="submenu-3-1" href="#">Promo Packs</a></li>
</ul>
<!-- Submenu 3-1 -->
<ul data-menu="submenu-3-1" class="menu__level">
<li class="menu__item"><a class="menu__link" href="#">Starter Kit</a></li>
<li class="menu__item"><a class="menu__link" href="#">The Essential 8</a></li>
<li class="menu__item"><a class="menu__link" href="#">Bolivian Secrets</a></li>
<li class="menu__item"><a class="menu__link" href="#">Flour Packs</a></li>
</ul>
<!-- Submenu 4 -->
<ul data-menu="submenu-4" class="menu__level">
<li class="menu__item"><a class="menu__link" href="#">Grain Mylks</a></li>
<li class="menu__item"><a class="menu__link" href="#">Seed Mylks</a></li>
<li class="menu__item"><a class="menu__link" href="#">Nut Mylks</a></li>
<li class="menu__item"><a class="menu__link" href="#">Nutri Drinks</a></li>
<li class="menu__item"><a class="menu__link" data-submenu="submenu-4-1" href="#">Selection</a></li>
</ul>
<!-- Submenu 4-1 -->
<ul data-menu="submenu-4-1" class="menu__level">
<li class="menu__item"><a class="menu__link" href="#">Nut Mylk Packs</a></li>
<li class="menu__item"><a class="menu__link" href="#">Amino Acid Heaven</a></li>
<li class="menu__item"><a class="menu__link" href="#">Allergy Free</a></li>
</ul>
</div>
</nav>
<div class="content">
<p class="info">Please choose a category</p>
<!-- Ajax loaded content here -->
</div>
</div>
<!-- /view -->
<script src="js/classie.js"></script>
<script src="js/dummydata.js"></script>
<script src="js/main.js"></script>
<script>
(function() {
var menuEl = document.getElementById('ml-menu'),
mlmenu = new MLMenu(menuEl, {
// breadcrumbsCtrl : true, // show breadcrumbs
// initialBreadcrumb : 'all', // initial breadcrumb text
backCtrl : false, // show back button
// itemsDelayInterval : 60, // delay between each menu item sliding animation
onItemClick: loadDummyData // callback: item that doesn´t have a submenu gets clicked - onItemClick([event], [inner HTML of the clicked item])
});
// mobile menu toggle
var openMenuCtrl = document.querySelector('.action--open'),
closeMenuCtrl = document.querySelector('.action--close');
openMenuCtrl.addEventListener('click', openMenu);
closeMenuCtrl.addEventListener('click', closeMenu);
function openMenu() {
classie.add(menuEl, 'menu--open');
}
function closeMenu() {
classie.remove(menuEl, 'menu--open');
}
// simulate grid content loading
var gridWrapper = document.querySelector('.content');
function loadDummyData(ev, itemName) {
ev.preventDefault();
closeMenu();
gridWrapper.innerHTML = '';
classie.add(gridWrapper, 'content--loading');
setTimeout(function() {
classie.remove(gridWrapper, 'content--loading');
gridWrapper.innerHTML = '<ul class="products">' + dummyData[itemName] + '<ul>';
}, 700);
}
})();
</script>
Css Kodları
/* Icons (made with Icomoon.io) */ /* Feather Icons by Cole Bemis */ @font-face { font-family: 'feather'; font-weight: normal; font-style: normal; src: url('../fonts/feather/feather.eot?1gafuo'); src: url('../fonts/feather/feather.eot?1gafuo#iefix') format('embedded-opentype'), url('../fonts/feather/feather.woff2?1gafuo') format('woff2'), url('../fonts/feather/feather.ttf?1gafuo') format('truetype'), url('../fonts/feather/feather.woff?1gafuo') format('woff'), url('../fonts/feather/feather.svg?1gafuo#feather') format('svg'); } .icon { font-family: 'feather'; font-weight: normal; font-style: normal; font-variant: normal; line-height: 1; text-transform: none; /* Better Font Rendering =========== */ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; speak: none; } .icon--arrow-left:before { content: '\e901'; } .icon--menu:before { content: '\e903'; } .icon--cross:before { content: '\e117'; } /* Menu styles */ .menu { position: fixed; top: 120px; left: 0; width: 300px; height: calc(100vh - 120px); background: #1c1d22; } .menu__wrap { position: absolute; top: 3.5em; bottom: 0; overflow: hidden; width: 100%; } .menu__level { position: absolute; top: 0; left: 0; visibility: hidden; overflow: hidden; overflow-y: scroll; width: calc(100% + 50px); height: 100%; margin: 0; padding: 0; list-style-type: none; } .menu__level--current { visibility: visible; } .menu__item { display: block; width: calc(100% - 50px); } .menu__link { font-weight: 600; position: relative; display: block; padding: 1em 2.5em 1em 1.5em; color: #bdbdbd; -webkit-transition: color 0.1s; transition: color 0.1s; } .menu__link[data-submenu]::after { content: '\e904'; font-family: 'feather'; position: absolute; right: 0; padding: 0.25em 1.25em; color: #2a2b30; } .menu__link:hover, .menu__link[data-submenu]:hover::after { color: #5c5edc; } .menu__link--current::before { content: '\00B7'; font-size: 1.5em; line-height: 0; position: absolute; top: 50%; left: 0.5em; height: 4px; color: #5c5edc; } [class^='animate-'], [class*=' animate-'] { visibility: visible; } .animate-outToRight .menu__item { -webkit-animation: outToRight 0.6s both cubic-bezier(0.7, 0, 0.3, 1); animation: outToRight 0.6s both cubic-bezier(0.7, 0, 0.3, 1); } @-webkit-keyframes outToRight { to { opacity: 0; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } } @keyframes outToRight { to { opacity: 0; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } } .animate-outToLeft .menu__item { -webkit-animation: outToLeft 0.6s both cubic-bezier(0.7, 0, 0.3, 1); animation: outToLeft 0.6s both cubic-bezier(0.7, 0, 0.3, 1); } @-webkit-keyframes outToLeft { to { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } } @keyframes outToLeft { to { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } } .animate-inFromLeft .menu__item { -webkit-animation: inFromLeft 0.6s both cubic-bezier(0.7, 0, 0.3, 1); animation: inFromLeft 0.6s both cubic-bezier(0.7, 0, 0.3, 1); } @-webkit-keyframes inFromLeft { from { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes inFromLeft { from { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .animate-inFromRight .menu__item { -webkit-animation: inFromRight 0.6s both cubic-bezier(0.7, 0, 0.3, 1); animation: inFromRight 0.6s both cubic-bezier(0.7, 0, 0.3, 1); } @-webkit-keyframes inFromRight { from { opacity: 0; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } @keyframes inFromRight { from { opacity: 0; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } } .menu__breadcrumbs { font-size: 0.65em; line-height: 1; position: relative; padding: 2.5em 3.75em 1.5em 2.5em; } .menu__breadcrumbs a { font-weight: bold; display: inline-block; cursor: pointer; vertical-align: middle; letter-spacing: 1px; text-transform: uppercase; color: #5c5edc; } .menu__breadcrumbs a:last-child { pointer-events: none; } .menu__breadcrumbs a:hover { color: #8182e0; } .menu__breadcrumbs a:not(:last-child)::after { content: '\e902'; font-family: 'feather'; display: inline-block; padding: 0 0.5em; color: #33353e; } .menu__breadcrumbs a:not(:last-child):hover::after { color: #33353e; } .menu__back { font-size: 1.05em; position: absolute; z-index: 100; top: 0; right: 2.25em; margin: 0; padding: 1.365em 0.65em 0 0; cursor: pointer; color: #2a2b30; border: none; background: none; } .menu__back--hidden { pointer-events: none; opacity: 0; } .menu__back:hover, .menu__back:focus { color: #fff; outline: none; } /* Open and close buttons */ .action { position: absolute; display: block; margin: 0; padding: 0; cursor: pointer; border: none; background: none; } .action:focus { outline: none; } .action--open { font-size: 1.5em; top: 1em; left: 1em; display: none; color: #fff; position: fixed; z-index: 1000; } .action--close { font-size: 1.1em; top: 1.25em; right: 1em; display: none; color: #45464e; } /* Example media query */ @media screen and (max-width: 40em) { .action--open, .action--close { display: block; } .menu { z-index: 1000; top: 0; width: 100%; height: 100vh; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); -webkit-transition: -webkit-transform 0.3s; transition: transform 0.3s; } .menu--open { -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } }
Js Kodları
/** * main.js * http://www.codrops.com * * Licensed under the MIT license. * http://www.opensource.org/licenses/mit-license.php * * Copyright 2015, Codrops * http://www.codrops.com */ ;(function(window) { 'use strict'; var support = { animations : Modernizr.cssanimations }, animEndEventNames = { 'WebkitAnimation' : 'webkitAnimationEnd', 'OAnimation' : 'oAnimationEnd', 'msAnimation' : 'MSAnimationEnd', 'animation' : 'animationend' }, animEndEventName = animEndEventNames[ Modernizr.prefixed( 'animation' ) ], onEndAnimation = function( el, callback ) { var onEndCallbackFn = function( ev ) { if( support.animations ) { if( ev.target != this ) return; this.removeEventListener( animEndEventName, onEndCallbackFn ); } if( callback && typeof callback === 'function' ) { callback.call(); } }; if( support.animations ) { el.addEventListener( animEndEventName, onEndCallbackFn ); } else { onEndCallbackFn(); } }; function extend( a, b ) { for( var key in b ) { if( b.hasOwnProperty( key ) ) { a[key] = b[key]; } } return a; } function MLMenu(el, options) { this.el = el; this.options = extend( {}, this.options ); extend( this.options, options ); // the menus ( ´s) this.menus = [].slice.call(this.el.querySelectorAll('.menu__level')); // index of current menu this.current = 0; this._init(); } MLMenu.prototype.options = { // show breadcrumbs breadcrumbsCtrl : true, // initial breadcrumb text initialBreadcrumb : 'all', // show back button backCtrl : true, // delay between each menu item sliding animation itemsDelayInterval : 60, // direction direction : 'r2l', // callback: item that doesn´t have a submenu gets clicked // onItemClick([event], [inner HTML of the clicked item]) onItemClick : function(ev, itemName) { return false; } }; MLMenu.prototype._init = function() { // iterate the existing menus and create an array of menus, more specifically an array of objects where each one holds the info of each menu element and its menu items this.menusArr = []; var self = this; this.menus.forEach(function(menuEl, pos) { var menu = {menuEl : menuEl, menuItems : [].slice.call(menuEl.querySelectorAll('.menu__item'))}; self.menusArr.push(menu); // set current menu class if( pos === self.current ) { classie.add(menuEl, 'menu__level--current'); } }); // create back button if( this.options.backCtrl ) { this.backCtrl = document.createElement('button'); this.backCtrl.className = 'menu__back menu__back--hidden'; this.backCtrl.setAttribute('aria-label', 'Go back'); this.backCtrl.innerHTML = ''; this.el.insertBefore(this.backCtrl, this.el.firstChild); } // create breadcrumbs if( self.options.breadcrumbsCtrl ) { this.breadcrumbsCtrl = document.createElement('nav'); this.breadcrumbsCtrl.className = 'menu__breadcrumbs'; this.el.insertBefore(this.breadcrumbsCtrl, this.el.firstChild); // add initial breadcrumb this._addBreadcrumb(0); } // event binding this._initEvents(); }; MLMenu.prototype._initEvents = function() { var self = this; for(var i = 0, len = this.menusArr.length; i < len; ++i) { this.menusArr[i].menuItems.forEach(function(item, pos) { item.querySelector('a').addEventListener('click', function(ev) { var submenu = ev.target.getAttribute('data-submenu'), itemName = ev.target.innerHTML, subMenuEl = self.el.querySelector('ul[data-menu="' + submenu + '"]'); // check if there's a sub menu for this item if( submenu && subMenuEl ) { ev.preventDefault(); // open it self._openSubMenu(subMenuEl, pos, itemName); } else { // add class current var currentlink = self.el.querySelector('.menu__link--current'); if( currentlink ) { classie.remove(self.el.querySelector('.menu__link--current'), 'menu__link--current'); } classie.add(ev.target, 'menu__link--current'); // callback self.options.onItemClick(ev, itemName); } }); }); } // back navigation if( this.options.backCtrl ) { this.backCtrl.addEventListener('click', function() { self._back(); }); } }; MLMenu.prototype._openSubMenu = function(subMenuEl, clickPosition, subMenuName) { if( this.isAnimating ) { return false; } this.isAnimating = true; // save "parent" menu index for back navigation this.menusArr[this.menus.indexOf(subMenuEl)].backIdx = this.current; // save "parent" menu´s name this.menusArr[this.menus.indexOf(subMenuEl)].name = subMenuName; // current menu slides out this._menuOut(clickPosition); // next menu (submenu) slides in this._menuIn(subMenuEl, clickPosition); }; MLMenu.prototype._back = function() { if( this.isAnimating ) { return false; } this.isAnimating = true; // current menu slides out this._menuOut(); // next menu (previous menu) slides in var backMenu = this.menusArr[this.menusArr[this.current].backIdx].menuEl; this._menuIn(backMenu); // remove last breadcrumb if( this.options.breadcrumbsCtrl ) { this.breadcrumbsCtrl.removeChild(this.breadcrumbsCtrl.lastElementChild); } }; MLMenu.prototype._menuOut = function(clickPosition) { // the current menu var self = this, currentMenu = this.menusArr[this.current].menuEl, isBackNavigation = typeof clickPosition == 'undefined' ? true : false; // slide out current menu items - first, set the delays for the items this.menusArr[this.current].menuItems.forEach(function(item, pos) { item.style.WebkitAnimationDelay = item.style.animationDelay = isBackNavigation ? parseInt(pos * self.options.itemsDelayInterval) + 'ms' : parseInt(Math.abs(clickPosition - pos) * self.options.itemsDelayInterval) + 'ms'; }); // animation class if( this.options.direction === 'r2l' ) { classie.add(currentMenu, !isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight'); } else { classie.add(currentMenu, isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight'); } }; MLMenu.prototype._menuIn = function(nextMenuEl, clickPosition) { var self = this, // the current menu currentMenu = this.menusArr[this.current].menuEl, isBackNavigation = typeof clickPosition == 'undefined' ? true : false, // index of the nextMenuEl nextMenuIdx = this.menus.indexOf(nextMenuEl), nextMenuItems = this.menusArr[nextMenuIdx].menuItems, nextMenuItemsTotal = nextMenuItems.length; // slide in next menu items - first, set the delays for the items nextMenuItems.forEach(function(item, pos) { item.style.WebkitAnimationDelay = item.style.animationDelay = isBackNavigation ? parseInt(pos * self.options.itemsDelayInterval) + 'ms' : parseInt(Math.abs(clickPosition - pos) * self.options.itemsDelayInterval) + 'ms'; // we need to reset the classes once the last item animates in // the "last item" is the farthest from the clicked item // let's calculate the index of the farthest item var farthestIdx = clickPosition <= nextMenuItemsTotal/2 || isBackNavigation ? nextMenuItemsTotal - 1 : 0; if( pos === farthestIdx ) { onEndAnimation(item, function() { // reset classes if( self.options.direction === 'r2l' ) { classie.remove(currentMenu, !isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight'); classie.remove(nextMenuEl, !isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft'); } else { classie.remove(currentMenu, isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight'); classie.remove(nextMenuEl, isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft'); } classie.remove(currentMenu, 'menu__level--current'); classie.add(nextMenuEl, 'menu__level--current'); //reset current self.current = nextMenuIdx; // control back button and breadcrumbs navigation elements if( !isBackNavigation ) { // show back button if( self.options.backCtrl ) { classie.remove(self.backCtrl, 'menu__back--hidden'); } // add breadcrumb self._addBreadcrumb(nextMenuIdx); } else if( self.current === 0 && self.options.backCtrl ) { // hide back button classie.add(self.backCtrl, 'menu__back--hidden'); } // we can navigate again.. self.isAnimating = false; }); } }); // animation class if( this.options.direction === 'r2l' ) { classie.add(nextMenuEl, !isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft'); } else { classie.add(nextMenuEl, isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft'); } }; MLMenu.prototype._addBreadcrumb = function(idx) { if( !this.options.breadcrumbsCtrl ) { return false; } var bc = document.createElement('a'); bc.innerHTML = idx ? this.menusArr[idx].name : this.options.initialBreadcrumb; this.breadcrumbsCtrl.appendChild(bc); var self = this; bc.addEventListener('click', function(ev) { ev.preventDefault(); // do nothing if this breadcrumb is the last one in the list of breadcrumbs if( !bc.nextSibling || self.isAnimating ) { return false; } self.isAnimating = true; // current menu slides out self._menuOut(); // next menu slides in var nextMenu = self.menusArr[idx].menuEl; self._menuIn(nextMenu); // remove breadcrumbs that are ahead var siblingNode; while (siblingNode = bc.nextSibling) { self.breadcrumbsCtrl.removeChild(siblingNode); } }); }; window.MLMenu = MLMenu; })(window);Demo için buraya, github üzerinden kodlara erişmek için ise buraya tıklamanız yetecektir.