Recreate Google Nexus Menu

Written by Kevin Liew on 15 Aug 2013
64,370 Views • Tutorials

Recently I stumbled upon Codrops' tutorial regarding the recreation of Google Nexus navigation menu. That was inspiring and such a creative navigation system from Google. So, I decided to create a jQuery version to challenge to myself.

I have been playing with the original Google Nexus Menu for a while. Based on my observation, I summarised its behaviour as below:

  • When hovering over the menu button, it will show icon only menu. And when you click on it, it will show full menu.
  • When it's showing icon only menu, the menu will hide itself on mouse out. Whereas when it's showing full menu, it won't hide itself until you click on the web page.
  • Icon only menu won't display secondary menu. Secondary menu will expand in full menu.
  • It uses mobile touchstart event and also responsive.

Now we understand its characteristics, and it will be much more easier to code it. It's not 100% imitation but it captured the essence of Google Nexus menu. Below is the screenshot of the final result:

One thing though, since it's coded with CSS3, as a result, it only works in browsers that support CSS3 transition.A pure Javascript version is possible, but I have decided to embrace the new technology :)

HTML

As you can see, we will have to build two menus, and add interactions and animation transitions on them. The structure of the menu is really straight forward and standard. We are using UL list for both.

Main Nav

<nav>
	<ul>
		<li><a href="#" class="icon icon-menu" id="btn-menu">Menu</a></li>
	</ul>
</nav>

Side Nav

<div id="sideNav">
	<ul>
		<li class="searchForm">
			<a href="#" class="icon icon-search">
				<span>
					<input type="text" placeholder="Search" class="search" />
				</span>
			</a>
		</li>
		<li><a href="#" class="icon icon-home"><span>Parent</span></a>
			<ul>
				<li><a href="#"><span>Children</span></a></li>
			</ul>
		</li>
	</ul>	
</div>	

CSS

We use fair a bit of CSS3 feature here such as transition, box-sizing and responsive layout. We will be using CSS3 transition to animate everything to take the advantage of GPU. I just reckon CSS3 animation is smoother than Javascript.

We put everything that need CSS transition together.

#sideNav,
#sideNav.showHalfMenu,
#sideNav.showFullMenu,
#sideNav ul ul li,
#sideNav.showFullMenu ul ul li	 {
	-webkit-transition: 0.2s ease;
	-moz-transition: 0.2s ease;			
	-ms-transition: 0.2s ease;						
	transition: 0.2s ease;		
}	

And the rest of the styling for the menu.

html.cursor {
	cursor: pointer;
}

nav {
	font-family: 'Roboto', sans-serif;
	width: 100%;
	height: 59px;
	border-bottom:1px solid #ddd;
	position: fixed;
	top:0;
	left:0;
	z-index:20;
	background-color:#ffffff;
}

	nav ul,
	#sideNav ul,
	#sideNav ul ul	{
		margin:0;
		padding:0;
		list-style:none;
	}
	
		nav li {
			margin:0;
			float:left;
			border-right:1px solid #ddd;
			font-size:18px;
		}
		
		nav a,
		#sideNav a {
			color:#5b6064;
			text-decoration:none;
			display:block;
			padding:10px 30px;
			height:59px;
			-webkit-box-sizing: border-box;
			-moz-box-sizing: border-box;
			-o-box-sizing: border-box;
			line-height:35px;
		}
		
		nav a:hover,
		#sideNav a:hover {
			color:#ffffff;
			background-color: #5b6064;
		}	
		
	#sideNav {
		position: fixed;
		left:-60px;
		top:59px;
		width: 60px;
		height:100%;
		border-right:1px solid #ddd;						
		background-color:#ffffff;
		overflow-y: auto;	
	}
		
		#sideNav.showHalfMenu {
			left:0;			
		}
		
		#sideNav.showFullMenu {
			left:0;
			width: 311px;		
		}

			#sideNav.showFullMenu ul ul li {
				height:59px;				
			}	
	
	
		#sideNav > ul {
			width: 100%;		
			padding-bottom:60px;	
		}
	
		#sideNav ul li {
			width: 100%;
			margin:0;		
			font-weight:300;
		}
		
		#sideNav ul li a {
			border-bottom:1px solid #ddd;		
			padding-left:70px;
		}
		
		#sideNav ul li span {
			position: relative;
			top:3px;
		}		
		
		#sideNav ul ul li {
			overflow:hidden;
			height: 0;				
		}
				

Search

One of the features of the menu is, the first menu item of the side nav has a search field that blended into the menu. Not sure if it's a good practice but it does look sleek. This is where I picked up another new thing, we actually allow to change placeholder color!

		#sideNav input.search {
			font-family: 'Roboto', sans-serif;			
			border:0;
			outline:0;
			font-weight:300;
			background:transparent;
			color:#5b6064;			
		}

				
		input.search::-webkit-input-placeholder {
			color:#5b6064;		
		}
		input.search:-moz-placeholder {
			color:#5b6064;		
		}
		input.search::-moz-placeholder {
			color:#5b6064;		
		}
		input.search:-ms-input-placeholder {
			color:#5b6064;
		}
		
		#sideNav li.searchForm:hover input.search:focus,
		#sideNav li.searchForm:hover input.search::-webkit-input-placeholder {
			color:#fff;		
		}		

Responsive

Not much here, we just want the menu fit well on mobile. It will look like this:

@media only screen and (min-width: 200px) and (max-width: 480px) {

	nav a,
	#sideNav a {
		padding:10px 15px;
	}	
	
	nav a#btn-menu {
		padding:10px 30px;	
	}

	#sideNav.showFullMenu,
	#sideNav.showFullMenu li,
	#sideNav.showFullMenu	a	 {
		width: 100%;
	}    

}

Javascript

And... the last section. Animation is just a matter of applying the right CSS classes at the right spot. We created a few functions such as showHalfMenu(), showFullMenu(), hideMenu() and toggleMenu so we can reuse them.

We also put in a mobile detection script I found from stackoverflow to make sure touch screen devices don't have to load desktop mouse events.

As usual, I added inline comments in the script to show how it's done.

$(function () {
	
	var GNmenu = {
		isMenuOpened: false,
		init : function () {

			var menuBtn = $('#btn-menu');
			
			// make it work on touch screen device and mouse click
			menuBtn.on('touchstart click', function (e) {
				e.stopPropagation();
				e.preventDefault();
				
				/* if the menu is half show, show the whole menu instead of hide it */
				if ($('#sideNav').hasClass('showHalfMenu') && !$('#sideNav').hasClass('showFullMenu')) {
					GNmenu.showFullMenu();
				} else {
					GNmenu.toggleMenu();												
				}
				
			});

			// only do all this if it's desktop
			if (!GNmenu.isMobile()) {

				menuBtn.bind('mouseover', function () {
						GNmenu.showHalfMenu();
				});
				
				menuBtn.bind('mouseout', function () {
						GNmenu.hideMenu();
				});	
			
				$('#sideNav').bind('mouseover', function () {
						GNmenu.showFullMenu();
				});											

				// this allow user to hide the menu by clicking on web page body
				GNmenu.bodyClick();
			}
						
			// search form
			// unbind the bodyClick event 
			$('.searchForm input[type=text]').focus(function () {
				$('html').unbind('click');	
			}).blur(function() {
				GNmenu.bodyClick();												
			});
													
		},
		
		bodyClick: function () {
		
			$('html').bind('click',function () {
				if (GNmenu.isMenuOpened) {
					GNmenu.hideMenu();														
				}
			});					
		
		},
		
		toggleMenu: function () {
			if (!GNmenu.isMenuOpened) {
				GNmenu.showFullMenu();
			} else {
				GNmenu.hideMenu();					
			}
		},
		
		showHalfMenu: function () {
			$('#sideNav').addClass('showHalfMenu');
			GNmenu.isMenuOpened = true;					
		},
		
		showFullMenu: function () {
			$('#btn-menu').addClass('icon-menu-active');
			$('#sideNav').addClass('showFullMenu');
			$('html').addClass('cursor');				
			GNmenu.isMenuOpened = true;							
		},
		
		hideMenu: function () {
			$('#btn-menu').removeClass('icon-menu-active');
			$('#sideNav').removeClass('showFullMenu showHalfMenu');		
			$('html').removeClass('cursor');						
			GNmenu.isMenuOpened = false;					
		},
		
		// Mobile Detection: http://stackoverflow.com/a/11381730				
		isMobile: function() {
			var check = false;
			(function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
			
			return check; 					
		}
		
	}
	
	// Run the Google Nexus Inspired Menu
	GNmenu.init();
	
});	

Conclusion

It's not hard to replicate something from sketch. What we need to do is to understand its behavior. Also, get your web developer tools ready, you will need to do a lot of code inspections for better understanding of how it works.

We just recreated a replica of Google Nexus menu with jQuery library. If you preferred something standalone, read tutorial from Codrops by Mary Lou, she did it without jQuery library.

Drop us a comment if you have any questions and stay tuned with us for more posts like this.

Demo Download
Join the discussion

Comments will be moderated and rel="nofollow" will be added to all links. You can wrap your coding with [code][/code] to make use of built-in syntax highlighter.

9 comments
atal 11 years ago
how you will highlight this menu if we are in particular section
Reply
Eddie Givens 11 years ago
Thank you for posting things! This is exactly what I was looking for on a project I'm working on. Thanks!
Reply
Nick 11 years ago
Well i was searching for the same answer but doesn't get the exactly result of the same. As of for the alternative i just hired http://www.markupcloud.com and i got the answer for the problem. Frankly speaking they are best at designing aspects. You can easily rely on them.
Reply
Sanjeev 11 years ago
Thanks for the article/tutorial/plugin. This is much easier to follow thean the others. I'll use it.
One modification, we can use font-awesome icons instead.

Add CSS
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">

Remove default icon [class="icon icon-xxxx"]
Add icon <i class="fa fa-location-arrow fa-2x fa-fw"></i> instead

Update CSS to add padding to menu text.
.sidebar-text {
padding-left:50px;
}

Reply
reyrey 11 years ago
sorry can you tell me what will do use like this {if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
Reply
ella 11 years ago
great tutorial! i've been looking for this version of mary lou's (codrops), thanks a lot! cheers!
Reply
Claudio Gelli 10 years ago
Congratulations, great tutorial. I adapted to my needs and worked really well on the desktop. But ...
When I tested on my phone (Galaxy S4) noticed that clicking outside the menu it would not close. I then tested with your demo and the same problem occurred.
Do you know what could be the problem?

Thanks in advance.


$(function () {

var GNmenu = {
isMenuOpened: false,
init : function () {

var menuBtn = $('#btn-menu');

// make it work on touch screen device and mouse click
menuBtn.on('touchstart click', function (e) {
e.stopPropagation();
e.preventDefault();

/* if the menu is half show, show the whole menu instead of hide it */
if (!$('#sideNav').hasClass('showFullMenu')) {
GNmenu.showFullMenu();
} else {
GNmenu.toggleMenu();
}

});


// search form
// unbind the bodyClick event
$('.searchForm input[type=text]').focus(function () {
$('html').unbind('click');
}).blur(function() {
GNmenu.bodyClick();
});


},

bodyClick: function () {
$('html').bind('click',function () {
if (GNmenu.isMenuOpened) {
GNmenu.hideMenu();
}
});

},

toggleMenu: function () {
if (!GNmenu.isMenuOpened) {
GNmenu.showFullMenu();
} else {
GNmenu.hideMenu();
}
},



showFullMenu: function () {
$('#btn-menu').addClass('icon-menu-active');
$('#sideNav').addClass('showFullMenu');
$('html').addClass('cursor');
GNmenu.isMenuOpened = true;
},

hideMenu: function () {
$('#btn-menu').removeClass('icon-menu-active');
$('#sideNav').removeClass('showFullMenu');
$('html').removeClass('cursor');
GNmenu.isMenuOpened = false;
},

// Mobile Detection: http://stackoverflow.com/a/11381730
isMobile: function() {
var check = false;
(function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);

return check;
}

}

// Run the Google Nexus Inspired Menu
GNmenu.init();

});

Reply
Kmachine 10 years ago
Hi,

I have a question. Is possible that the submenu (in your example is web design, web developer) only appears when the user click or hover in the menu item that contains the subcategory?

Thank you very much!
Reply
Martin 9 years ago
Hello, my name is Martin and I'm working on my project. I'm very impressed by Google Nexus Menu and I use Wordpress. However I am not a developer I don't know how to let it show on my page. I think if I had a shortcode I would be able to make it. Also, there is always error when I want to upload it in .zip file like plugin. Can anyone help me? I really don't know how to get it to my page :/
Reply