Multi-Level Push Menu

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

From our sponsor: Looking for an intuitive whiteboard style project management tool? Give Shortcut a try for free.

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>
			<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>
						<li class="icon icon-arrow-left">
							<a class="icon icon-phone" href="#">Mobile Phones</a>

							<div class="mp-level">
								<h2>Mobile Phones</h2>
									<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>

						<li class="icon icon-arrow-left">
							<!-- ... -->
						<li class="icon icon-arrow-left">
							<!-- ... -->
			<li><!-- ... --></li>
			<!-- ... -->
<!-- /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">
			<!-- ... -->
		<!-- /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:

.scroller {
	height: 100%;

.scroller {
	overflow-y: scroll;

.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:

Mary Lou

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.

Stay up to date with the latest web design and development news and relevant updates from Codrops.

Feedback 261

Comments are closed.
    • look at mlPushMenu prototype…
      _init : function() {
      // if menu is open or not = false; // just change it to true

  1. it looks great on firefox ,but I’m afraid this is useless if you ignore one of the most popular browser today,chrome. Anyway good job!

  2. Hello,

    first very nice work, but i have question :

    how i can set the menu perma open, also without a Initiator to open/Close,

    because i Need use it in Frame, 🙁

    thx for help

  3. The fallback options aren’t working for browsers that don’t have 3d transforms. Has anyone come up with a fix?

  4. Hi there. Does anyone perhaps know how I can keep the state of the link clicked to show where I am in the menu levels. I need to be able to store the state of the menu as it is clicked because it keeps going to the top level when I click a link inside the menu. I tried disabling the self._resetMenu(); in mlpushmenu.js but it did not work as I figured that might be what to disable in the code.

    When a link is clicked it should just show the category title of links you are on because I will be using a breadcrumb to display the depth of the levels.

    Help will be awesome 🙂

  5. Hi!

    This is an amazing menu. I’m currently attempting to implement into a WordPress Theme I have going. At current I’ve managed to get it implemented fine. There were issues with the Linecons Icons so they were replaced with Font-Awesome Icons instead (matching the Theme) and have resolved that issue.

    The main issue is that upon first opening the website you are unable to click links / scroll the page down at all until you have clicked the menu icon to trigger the menu and then close it again. It appears as if the ‘overlay’ is preventing anything from happening. Some research seemed to indicate it was due to a bug with the version of jQuery being called which using the Fix has not worked.

    The site in question is as well as (same issue, different sites, same theme).

    If there are any pointers that would be great! 🙂


    • Did you managed the WordPress solution with multiple WordPress Menus or did you only used one?

      Thanks for the reply 🙂

  6. First, I would like to congratulate with Mary Lou for this awesome menu, and I want to ask you all, if it’s possible to close it each time I click on a menu item.

    I don’t like to:
    > open the menu
    >> click on the XPAGE anchor
    >>> and then click again on the body (or on the close icon) in order to see the XPAGE content.

    I’d like to:
    > open the menu
    >> click on the XPAGE anchor
    >>> see the menu closing by itself and showing the XPAGE content.

    Any help would be appreciated.
    Thank You in advance,

  7. Hi all….Still having a problem with the push-menu not being able to keep the state of where the push menu is without it resetting itself to the top level of the menu. If the following code can be used to keep the menu open at all times:

    var mainMenu = new mlPushMenu( document.getElementById( ‘mp-menu’ ), document.getElementById( ‘trigger’ ) );


    is it not possible to do something similar like this to keep a sub-level open even when a link is clicked? but obvoiusly changing the id tags accordingly?

    var subMenu = new mlPushMenu( document.getElementById( ‘mp-menu’ ), document.getElementById( ‘trigger’ ) );


    • Crispian, Did you ever get this figured out? Some info would be great
      + Any idea if this menu can be implemented horizontally?

    • Go to the plugin file:
      _resetMenu : function() { this._setTransform('translate3d(0,0,0)'); this.level = 0; // remove class mp-pushed from main wrapper classie.remove( this.wrapper, 'mp-pushed' ); this._toggleLevels(); = false; },

      Comment the line this._toggleLevels(); this way when you close the menu, the state of the menu will be keeped , no matter what level you where before closing.

      Using this, combined with the modification by @csanchezriballo (on comment page three) jsfidlle you can add a "mp-close class" dynamically to your links when you click on them, and close the menu without reseting the menu state.

      hope that helps!

  8. Great code again very nice looking.
    Any chance of this working horizontally instead of vertical?
    And how do I keep the menu displayed and as Crispian was asking is there anyway to maintain the menu location once a submenu item is activated?

  9. Oof… I loved the look, loved the smoothness of the menu and how it flowed and was multilayered… But alas my dreams were broken because the actual content of the page you’re looking at on a mobile device gets significantly choppy scrolling, and my phone is new and powerful! That’s it, my constructive criticism. Also, should add that it seems the component.css is the culprit.

  10. Easy and nice menu experiment 😉 . How can we change the position from left to right display? HELP!!!!!!!! ;'(

  11. Running the menu on a site, and it gets really slow and choppy scrolling. (tested on iphone 4s, 5 and 5s and samsung galaxy tab)
    Has anyone figured out a solution for it?

    -webkit-overflow-scrolling: touch; doesn’t seem to do the job.

    Thanks in advance

  12. I’m hoping someone can help me with issues on a site that does require scrolling. Has anyone used this successfully or made any changes that allowed for smoother scrolling of the site?