Multi-Level Push Menu

An experimental push menu with multi-level functionality that allows endless nesting of navigation elements.
MultiLevelPushMenu

From our monthly sponsor: Create your beautiful portfolio website with Squarespace. Start your free trial.Advertisement

Today we want to share another menu experiment with you. For sure you are familiar with the off-canvas navigation on mobile apps and the implementations for responsive websites like the one by David Bushell. We tried to explore the possibility of creating a nested multi-level menu, something that could be quite useful for menus with lots of content (like navigations of online stores). The result is a “push” menu that can (theoretically) contain infinite nested sub-menus. When opening a sub-level, the whole navigation pushes the content more, allowing a slice of the parent menu to be visible. Optionally, this slice can be visible or not, in which case the sub-menu will simply cover its parent.

Working with nested structures is quite tricky because when we, for example, move the parent then all children will of course move as well. So we are using a couple of tricks that will maintain the right 3D translates for the sub-menus and their children. The main idea is to increment the value for the translate so that we guarantee that the sub-levels are not shown once we push everything a bit more for showing the slices of the parents. This is of course not necessary in the case where the sub-menu is covering the parent menu.

Please note that we are using 3D Transforms which only work in modern browsers. You will find a fallback example for non-supporting browsers in the end of the component.css file where we simply show the first level menu. The same we do for the no JS case.

We are using the following nested structure for the menu:

<!-- mp-menu -->
<nav id="mp-menu" class="mp-menu">
	<div class="mp-level">
		<h2 class="icon icon-world">All Categories</h2>
		<ul>
			<li class="icon icon-arrow-left">
				<a class="icon icon-display" href="#">Devices</a>
				
				<div class="mp-level">
					<h2 class="icon icon-display">Devices</h2>
					<ul>
						<li class="icon icon-arrow-left">
							<a class="icon icon-phone" href="#">Mobile Phones</a>

							<div class="mp-level">
								<h2>Mobile Phones</h2>
								<ul>
									<li><a href="#">Super Smart Phone</a></li>
									<li><a href="#">Thin Magic Mobile</a></li>
									<li><a href="#">Performance Crusher</a></li>
									<li><a href="#">Futuristic Experience</a></li>
								</ul>
							</div>

						</li>
						<li class="icon icon-arrow-left">
							<!-- ... -->
						</li>
						<li class="icon icon-arrow-left">
							<!-- ... -->
						</li>
					</ul>
				</div>
			</li>
			<li><!-- ... --></li>
			<!-- ... -->
		</ul>
	</div>
</nav>
<!-- /mp-menu -->

…where each level is wrapped into a division with the class mp-level.

Normally, we would have used fixed positioning for a menu of this kind but since there is quite an peculiar “problem” with that (transforms will make it behave like an absolute positioned element), we’ll have to use absolute positioning which will leave us with some unwanted behavior of the site (scrolling of menu and dependence of document height). So we’ve used a little trick to avoid the menu being scrollable or to be cut off if the site’s content is too short by using the following page structure:

<div class="container">
	<!-- Push Wrapper -->
	<div class="mp-pusher" id="mp-pusher">

		<!-- mp-menu -->
		<nav id="mp-menu" class="mp-menu">
			<!-- ... -->
		</nav>
		<!-- /mp-menu -->

		<div class="scroller"><!-- this is for emulating position fixed of the nav -->
			<div class="scroller-inner">
				<!-- site content goes here -->
			</div><!-- /scroller-inner -->
		</div><!-- /scroller -->

	</div><!-- /pusher -->
</div><!-- /container -->

Where we set the following styles for the elements:

html, 
body, 
.container,
.scroller {
	height: 100%;
}

.scroller {
	overflow-y: scroll;
}

.scroller,
.scroller-inner {
	position: relative;
}

.container {
	position: relative;
	overflow: hidden;
	background: #34495e;
}

This will allow the content to be scrolled when the menu is closed and it will also make the menu being 100% of the window height. Basically, we are emulating what fixed positioning would do here.

This is how the plugin can be called:

new mlPushMenu( document.getElementById( 'mp-menu' ), document.getElementById( 'trigger' ) );
Or, if the submenus should cover the previous levels: 
new mlPushMenu( document.getElementById( 'mp-menu' ), document.getElementById( 'trigger' ), {
	type : 'cover'
} );

For the demos we are using the beautiful Linicons iconfont by Sergey Shmidt created with the help of the IcoMoon app.

We hope you enjoy this menu and find it useful.

Tagged with:

ML is a freelance web designer and developer with a passion for interaction design. She studied Cognitive Science and Computational Logic and has a weakness for the smell of freshly ground peppercorns.

http://www.codrops.com

Receive our bi-weekly Collective or official newsletter right in your inbox.

CSS Reference

Learn about all important CSS properties from the basics with our extensive and easy-to-read CSS Reference.

It doesn't matter if you are a beginner or intermediate, start learning CSS now.

Feedback 261

Comments are closed.
  1. I don’t suppose there is a simplified version of this, seems a lot to go through in order to use anything here. Maybe just the first action on it’s own at all??

    Thanks

  2. Its great. Thank yo.u Just finished rebuilding it on my local computer. And its look really good 😀

  3. Unfortunately this does not do anything on ie10 mobile. You can’t even read all of the text as it’s pushed out of the viewport to the right.

  4. Is it possible to have a default slide open when inside a specific section etc? Thoughts?

    • I have a same problem. I see that nobodys care when u write about it. This line could do something with our issue // if menu is open or not this.open = false; but when you change false on true notting happens or am I bad searching ?

    • Add the following line wherever u want into the menu to display: Close

      Change js files like this

      // the menu should close if clicking somewhere on the body (excluding clicks on the menu)
      //document.addEventListener( self.eventtype, function( ev ) {

      //default behaviour replaced, the menu should close by clicking close menu buttons, uncomment the previous lines to keep both
      $(‘.mp-close’).mousedown( function( ) {
      if( self.open ) {
      bodyClickFn( this );
      }
      } );

    • @charlie Thanks for the help on this. I added the following code like you said , but the close button in the menu only works if I OPEN the menu, Then click the BODY to close the menu, then open the menu again (note now there is a # at the end of the url.) Then when I click the CLOSE button in the menu it works…? So it kinda works, but not really. Can you help straighten this out? I think it has something to do with (bodyClickFn) but not sure what to put in place of that.

      Here is my html code:
      <a href=”#” rel=”nofollow”> Close Menu</a>

      And the js:
      // the menu should close if clicking somewhere on the body (excluding clicks on the menu)
      document.addEventListener( self.eventtype, function( ev ) {
      if( self.open && !hasParent( ev.target, self.el.id ) ) {
      bodyClickFn( this );
      $(‘.mp-close’).mousedown( function( ) {
      if( self.open ) {
      bodyClickFn( this );
      }
      });
      }
      });
      }
      });

    • I made a fast JSFiddle with a few mods.

      Just look for the var


      closeClass : 'mp-close'

      and the function


      // close menu
      // by clicking on a close link
      this.levelClose.forEach( function( el, i ) {
      el.addEventListener( self.eventtype, function( ev ) {
      bodyClickFn( this );
      } );
      } );

      Define a link in each level whatever u want in order to close the menu.

      <a class="mp-close" href="#">Close</a>

      You could play with the css in order to make it like a button floating right the h2 title or whatever u want.

    • @csanchezriballo

      Thank you. I will use your code from fiddle and working like a charm 🙂

  5. Really enjoying some of blueprints. Background change with scroll-er is great. Enjoying things like jQuery Easing effects, pushing ideas far more 🙂

  6. since this have a problem with older browser is there a possibility that we can create multi level push menu into jquery? Using animate function for 2 divs moving to right? Those 2 divs are menu and content? for ex $(‘.menu’).click(function(){ $(this, ‘#content’).animate({left:’250px’});}); ?? then add effects like easeIn,etc

  7. very nice indeed!

    I was wondering if there is a way to have 2 push menu on the same page. So you can trigger each menu separately.
    I duplicated the script as follow:
    new mlPushMenu( document.getElementById( ‘mp-menu’ ), document.getElementById( ‘trigger’ ), {
    type : ‘cover’
    } );
    new mlPushMenu( document.getElementById( ‘mp-menu2’ ), document.getElementById( ‘trigger2’ ), {
    type : ‘cover’
    } );
    and i duplicated the nav and changed the id to have a nav with id mp-mennu2 and a trigger with if trigger2.

    but only the second trigger is working. If i click on the first trigger, it still opens mp-menu2.

    Does anybody have an idea on how to get that result.
    thks

  8. Is there a way that on ex.3 the nav bar can always visible and then the layers can push over it. I am wanting it to be a fixed side nav always visible rather than having to click the “menu” button to reveal the nav bar.

  9. Getting this error when I click on the icon to expand the menu. Using FF.

    TypeError: el is null
    el.style.WebkitTransform = val;

    mlpushmenu.js line 221

    Any suggestions?