Multi-Level Push Menu

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

From our monthly sponsor: Automate manual QA and catch visual bugs with Percy’s all-in-one visual testing and review platform.

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. Is there an easy way to have this function the same way, but open on the right side of the screen?

  2. Is it me or can’t I visit the “Television” page? All it seems to do is load the children.

    While it looks nice and has it’s merits, it seems quite useless if I can’t get to the “television” page from the menu.

    • Look at my latest comment. When using the idea with the sidr plugin you can have the navigation also on the right side of the screen.

      cheers

    • Using Developer tools get …

      CSS3111: @font-face encountered unknown error.
      zLhfkPOm_5ykmdm-wXaiuw.eot

  3. Hi,
    I had the problem that this navigation doesnt work for larger menus and on the internet explorer below 9. I found this idea really neat so I tried out a complete new approach making the navigation work in older browsers (without css3 animations). I used a combination of the jquery sidr plugin, some jquery click-events and css3 transitions. The result: less html-code for the navigation itself (it’s now a simple ul-list), less css classes, less js struggle.
    But anyway, thanks a lot for the inspiration!!!

    Cheers

    • Same boat as Adam and Thomas. Would really love to see your solution to this. I’m having the same issues.

    • Hi Dietmar,

      please could you share your optimized code?
      It would be a great help, thanks in advance!

    • I know it’s been a while since this have been posted, but I’m currently working on implementing this awesome menu. However, I’m struggling quite a bit with IE8 & 9. So would really love to hear a little more about your experiences with optimizing the plugin for IE. Thanks in advance.

      /Aske

  4. This is a really elegant solution for large navigations.

    Don’t forget -webkit-overflow-scrolling: touch; to apply smooth, elastic scrolling to the scroller wrapper on iOS.

    Also instead of height: 100% try min-height:100% . That will allow the menu to scroll vertically for long menus.

    • Do you have already an answer on this? At the moment I’m stuck with the same problem… I would love to use it for my internal app – so there is no need to open/close the main menu…

      Thanks

      Oli

  5. Hello Mary. Interesting menu here. I’ve a question. On mobile devices, does this support swipe feature just like apps menus do? Facebook Youtube etc. I wanna be able to open/close it not just by touching the hamburger icon, but also to swipe left/right. Please reply. Thanks.

  6. Has anyone tried any functionality for active links? For instance, a person is on a page that is linked from this menu and when they go to the menu the section with the link is open and the link is highlighted.

  7. To make it scroll I added:

    .mp-level {
    overflow-y: hidden;
    }

    .mp-level-open {
    overflow-y: auto;
    }

    .mp-level-overlay {
    overflow-y: inherit;
    }

    .mp-menu ul {
    overflow-y: hidden;
    }

    • I added this to make the menu scroll:

      .mp-level.mp-level-open:not(.mp-level-overlay) > ul {
      height: 100%;
      overflow-y: scroll;
      -webkit-overflow-scrolling:touch;
      }

    • This causes a white block to appear left of the menu when hitting the back button in Chrome, IE and Firefox. I didn’t try it on any mobile browsers. It only happens for a fraction of a second, but it’s distracting and causes the whole menu to lag a bit. (But, sorry, I haven’t found a better solution).

    • Also, lei cao’s solution won’t scroll to all content if there is a back button or heading above the ul because the 100% height will be the full height of the window while the menu will not start at 0 (it will instead start after the heading and back button).. Adding padding to the bottom works in Chrome, but not IE or Firefox. Again, I haven’t found a better solution just yet. I’ll let you know if I do..

    • Hi, I had the same situation. Needed to scroll the menu, but with
      .mp-level { overflow: scroll; }
      Had a blank bar in the left.
      This code made the scroll works, but in android has some weird reactions that makes all menus appear and disappear. And, in iOS and Android the first menu returns to top when open submenus.
      However, this is the best solution to scrolling menu that have found, thanks a lot.

  8. Thanks for this amazing idea. Btw how can I add triggers to open/close the menu with keys? Like M to open and ESC to close.

  9. I’ve loving this push menu – thank you!

    One question, is there an easy way to have the first mp-level of the menu visible at page load without needing to use the open/close menu trigger?

    • Hi Ryan,

      For having the menu load automatically I did something like this:

      mainMenu = new mlPushMenu( document.getElementById( 'mp-menu' ), document.getElementById( 'trigger' ), {
      type : 'cover'});
      mainMenu._openMenu();

      And that should sort it for you.

    • Hi Jon. You code works perfectly. I tried it on something I am working on. However what I am trying to do is when I click on links to other pages it still reverts back to the top level of the menu. It doesn’t show where you are exactly inside the menu even if you are on a different page. Do you perhaps know how this can be done?

      I have tried using classes with the <a> tags to see if it will work but no luck….Help will really be great.

      And to Mary Lou…Great work. Very impressive 🙂

    • Hi Crispian,

      Sorry for the late reply, I have actually just solved this using jQuery. You may need to do some adjusting, but this solution works for me.


      jQuery(document).ready(function(){
      jQuery("#mp-menu .menu-item a").each(function(){
      if(jQuery(this).attr('href') == window.location.href ){
      jQuery(this).parents('.mp-level').toggleClass('mp-level-open');
      jQuery('#mp-menu > .mp-level').toggleClass('mp-level-open');
      }
      });
      });

    • Hy Jon!

      Can you put a link (http://jsfiddle.net) with example of what you did, or a example that has a button that when clicked, automatically opens the menu in a specific category?

      Example: “Button – Go to Devices / Televisions”

      Because I need control the levels of the menu, example, if I´m on Televisions page, when i click on menu, the menu open in the Televisions Category.

      Thank you very much.

      Hope you can help 😉

  10. I just noticed an issue…not sure if anyone else is having the same issue. But…when I attempt to go to a page anchor via a menu option with sublevels, it doesn’t go to the anchor. So below…the page won’t go to the ‘THISONE!!!!’ anchor. Why won’t it?

    All Categories

    Devices

    Devices

    • @jDub: I am having the exact same issue. Everytime I click on a link of a sub menu. It goes to the page but it does not show or display where you are in the menu or basically the heading of the menu does not stay.

  11. Hi,
    I love the ease and design of your menu 🙂 but wondered what I would need to “fire” to get the menu to close once you have selected a menu option?
    Thank you for your help in advance and great menu 🙂 (again).

    Mark

    • Actually I just realised I could use “variabe”.resetMenu() on the click function of the – Doh

      Thank you anyway 🙂

      I have it working really well with Backbone Marionette 🙂

      Thank you again…