Animated Content Menu with jQuery

Today we will create a slick animated content menu with jQuery for a restaurant theme. The menu items will be animated and when clicked, a content area with more information […]

Today we will create a slick animated content menu with jQuery for a restaurant theme. The menu items will be animated and when clicked, a content area with more information will appear. Also, he background image is going to change according to which menu item was clicked.

The main idea is to have a restaurant menu with the different menu categories displayed. Each content are will have some foods/drinks listed. Once a content box is shown, the menu items are going to disappear. If we click on the close button to close the box, the menu items will be shown again.

So, let’s get started.

Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.

The Markup

The HTML structure is going to contain some elements for the background image, the grid overlay, the loading icon and the main menu. The Markup for the background is going to look like this:

<div id="ac_background" class="ac_background">
	<img class="ac_bgimage" src="images/Default.jpg" alt="Background"/>
	<div class="ac_overlay"></div>
	<div class="ac_loading"></div>
</div>

As a default background image when the page loads, we will have the image Default.jpg. Whenever we click on a menu item, we are going to change that image by adding another one and making the current one disappear.

The menu content will be wrapped by a div with the class “ac_content”. There will be a title and an unordered list for the items wrapped by a div with the class “ac_menu”:

<div id="ac_content" class="ac_content">
	<h1><span>Cafe + Bar</span>Dhalia</h1>
	<div class="ac_menu">
		<ul>
			<li>
				<a href="images/Appetizers.jpg">Appetizers</a>
				<div class="ac_subitem">
					<span class="ac_close"></span>
					<h2>Appetizers</h2>
					<ul>
						<li>
							A wonderful serenity has taken possession 
							of my entire soul, like these sweet mornings 
							of spring which I enjoy with my whole heart.
						</li>
						<li>Lobster Bisque</li>
						<li>Smoked Salmon Terrine</li>
						<li>Tuna Ceviche</li>
						<li>Wild Mushroom Flan</li>
						<li>Almond Bruschetta</li>
						<li>Green Chilli Canapee</li>
						<li>Artichoke Rucula Salad</li>
					</ul>
				</div><!-- ac_subitem-->
			</li>
			...
		</ul>
	</div><!-- ac_menu -->
</div><!-- ac_content -->

For each menu item, we will have a div “ac_subitem” which will contain the submenu content box. Inside of that box we will place another list (but it can be any other kind of content).

The link element in the main menu will have a href attribute pointing to the image that we will make appear in the background then.

Let’s take a look at the style.

The CSS

In the beginning of our CSS we will import a reset.css:

@import url('reset.css');

We will make the body black since we want to fade out the background images and make them appear darker while we do that:

body{
	background:#000;
	color:#fff;
	font-family: 'PT Sans Narrow', Arial, sans-serif;
	text-transform:uppercase;
}

The links are going to be white:

a{
	color:#fff;
	text-decoration:none;
}

The background image is going to be fixed and we don’t want to show it in the beginning – we want to take care of the appearance using JavaScript. The positioning and the size of the image is also going to be done dynamically, since we want to place it regarding to the size of the user’s window:

img.ac_bgimage{
	position:fixed;
	left:0px;
	top:0px;
	opacity:0.8;
	display:none;
}

The overlay is going to be placed on top of the image. It’s a repeated pattern that will give the image a neat look:

.ac_overlay{
	width:100%;
	height:100%;
	position:fixed;
	top:0px;
	left:0px;
	background:transparent url(../images/pattern.png) repeat top left;
}

We will place the loading icon in the top right corner of the page and it will appear in the beginning while we load the images:

.ac_loading{
	position:fixed;
	top:10px;
	right:10px;
	background:#000 url(../images/loader.gif) no-repeat center center;
	width:50px;
	height:50px;
	border-radius:10px 10px 10px 10px;
	z-index:999;
	opacity:0.7;
	display:none;
}

The main content div will also be fixed and placed in the middle of the screen. We set the top value to 50% and subtract half of its height from that position by setting the margin to -65 pixel. Like that we place it exactly in the middle of the webpage:

.ac_content{
	position:fixed;
	height:90px;
	width:100%;
	top:50%;
	left:0px;
	margin-top:-65px;
}

The heading to the left will have a black semi-transparent background and we will style the h1 and the span differently. We will also add a 1 pixel margin to the right in order to separate it a bit from the menu wrapper:

.ac_content h1{
	background:transparent url(../images/bg_menu.png) repeat top left;
	display:block;
	float:left;
	width:90px;
	height:50px;
	padding:20px;
	font-size:36px;
	font-weight:bold;
	line-height:20px;
	margin-right:1px;
}
.ac_content h1 span{
	display:block;
	font-weight:normal;
	font-size:14px;
}

The main menu wrapper will have the same background like the heading and we will give it an initial width of 0 pixel. In the JavaScript we will then animate the width when we load the page to fit the width of the window:

.ac_menu{
	background:transparent url(../images/bg_menu.png) repeat top left;
	float:left;
	position:relative;
	height:90px;
	width:0px; 
}

The unordered list which will contain the menu items will float right:

.ac_menu > ul{
	float:right;
}

The list items will have a specified height and the overflow will be set to hidden since we want to animate the link elements to appear from the bottom. If we wouldn’t set the overflow to hidden, we would be able to see the link elements under the menu.

.ac_menu > ul > li{
	float:left;
	position:relative;
	height:90px;
	overflow:hidden;
}

The link elements will initially be hidden by setting the margin top to 60 pixel and the opacity to 0. We will animate that margin to 0 pixel and make them opaque, in order for the items to appear, each one with a little delay, creating an interesting effect.

.ac_menu > ul > li a{
	margin-top:60px;
	opacity:0;
	display:block;
	height:90px;
	padding:0px 10px;
	text-align:center;
	line-height:90px;
	outline:none;
	font-size:18px;
	font-weight:bold;
	text-shadow:1px 1px 1px #000;
}

The subitem boxes will have a width of 400 pixel and an initial height of 0 pixel. We will animate that height and the top margin in order for each box to appear from the middle:

.ac_subitem{
	width:400px;
	height:0px; /* animate to 400px */
	top:50%;
	right:0px;
	margin-top:0px; /* animate to -200px */
	position:fixed;
	z-index:99;
	overflow:hidden;
	background:transparent url(../images/bg_menu.png) repeat top left;
}

Let’s give some styling to the content of the subitems:

.ac_subitem h2{
	font-size:22px;
	font-weight:bold;
	color:#fff;
	padding: 40px 0px 0px 40px;
	text-shadow:0px 0px 1px #000;
}
.ac_subitem ul{
	padding:0px 40px;
}
.ac_subitem ul li{
	margin:10px 0px;
}
.ac_subitem ul li:first-child{
	font-size:14px;
	text-transform:none;
	border-bottom:1px dotted #333;
	padding-bottom:15px;
	margin-bottom:15px;
}

And let’s place the little closing cross at the top right corner:

span.ac_close{
	float:right;
	margin:10px;
	width:11px;
	height:12px;
	cursor:pointer;
	background:transparent url(../images/close.png) no-repeat top left;
	opacity:0.4;
}
span.ac_close:hover{
	opacity:1.0;
}

And that’s all the style. Let’s add the magic!

The JavaScript

For the effects will will be using some easing, so we will need to include the jQuery Easing Plugin after including the jQuery script.

Let’s first cache some elements:

var $ac_background	= $('#ac_background'),
$ac_bgimage		= $ac_background.find('.ac_bgimage'),
$ac_loading		= $ac_background.find('.ac_loading'),

$ac_content		= $('#ac_content'),
$title			= $ac_content.find('h1'),
$menu			= $ac_content.find('.ac_menu'),
$mainNav		= $menu.find('ul:first'),
$menuItems		= $mainNav.children('li'),
totalItems		= $menuItems.length,
$ItemImages		= new Array();

We want to preload all the image, so let’s add all the image sources that we have in the href attributes of the link elements in the menu items, and also the one of the current image:

$menuItems.each(function(i) {
	$ItemImages.push($(this).children('a:first').attr('href'));
});
$ItemImages.push($ac_bgimage.attr('src'));

Then, let’s define our main function:

var Menu 			= (function(){
	var init				= function() {
		loadPage();
		initWindowEvent();
	},
	loadPage			= function() {
		/*
			1- loads the bg image and all the item images;
			2- shows the bg image;
			3- shows / slides out the menu;
			4- shows the menu items;
			5- initializes the menu items events
		 */
		$ac_loading.show(); //show loading status image
		$.when(loadImages()).done(function(){
			$.when(showBGImage()).done(function(){
				//hide the loading status image
				$ac_loading.hide();
				$.when(slideOutMenu()).done(function(){
						$.when(toggleMenuItems('up')).done(function(){
						initEventsSubMenu();
					});
				});
			});
		});
	},
	showBGImage			= function() {
		return $.Deferred(
		function(dfd) {
			//adjusts the dimensions of the image to fit the screen
			adjustImageSize($ac_bgimage);
			$ac_bgimage.fadeIn(1000, dfd.resolve);
		}
	).promise();
	},
	slideOutMenu		= function() {
		/* calculate new width for the menu */
		var new_w	= $(window).width() - $title.outerWidth(true);
		return $.Deferred(
		function(dfd) {
			//slides out the menu
			$menu.stop()
			.animate({
				width	: new_w + 'px'
			}, 700, dfd.resolve);
		}
	).promise();
	},
		/* shows / hides the menu items */
		toggleMenuItems		= function(dir) {
		return $.Deferred(
		function(dfd) {
			/*
			slides in / out the items. 
			different animation time for each one.
			*/
			$menuItems.each(function(i) {
						var $el_title	= $(this).children('a:first'),
							marginTop, opacity, easing;
						if(dir === 'up'){
							marginTop	= '0px';
							opacity		= 1;
							easing		= 'easeOutBack';
						}
						else if(dir === 'down'){
							marginTop	= '60px';
							opacity		= 0;
							easing		= 'easeInBack';
		}
				$el_title.stop()
				.animate({
									marginTop	: marginTop,
									opacity		: opacity
								 }, 200 + i * 200 , easing, function(){
					if(i === totalItems - 1)
						dfd.resolve();
				});
			});
		}
	).promise();
	},
	initEventsSubMenu	= function() {
		$menuItems.each(function(i) {
			var $item		= $(this), // the <li>
			$el_title	= $item.children('a:first'),
			el_image	= $el_title.attr('href'),
			$sub_menu	= $item.find('.ac_subitem'),
			$ac_close	= $sub_menu.find('.ac_close');
			
			/* user clicks on a menu item */
			$el_title.bind('click.Menu', function(e) {
					$.when(toggleMenuItems('down')).done(function(){
					openSubMenu($item, $sub_menu, el_image);
				});
				return false;
			});
			/* closes the submenu */
			$ac_close.bind('click.Menu', function(e) {
				closeSubMenu($sub_menu);
				return false;
			});
		});
	},
	openSubMenu			= function($item, $sub_menu, el_image) {
		$sub_menu.stop()
		.animate({
			height		: '400px',
			marginTop	: '-200px'
		}, 400, function() {
			//the bg image changes
			showItemImage(el_image);
		});
	},
	/* changes the background image */
	showItemImage		= function(source) {
		//if its the current one return
		if($ac_bgimage.attr('src') === source)
			return false;
				
		var $itemImage = $('<img src="'+source+'" alt="Background" class="ac_bgimage"/>');
		$itemImage.insertBefore($ac_bgimage);
		adjustImageSize($itemImage);
		$ac_bgimage.fadeOut(1500, function() {
			$(this).remove();
			$ac_bgimage = $itemImage;
		});
		$itemImage.fadeIn(1500);
	},
	closeSubMenu		= function($sub_menu) {
		$sub_menu.stop()
		.animate({
			height		: '0px',
			marginTop	: '0px'
		}, 400, function() {
			//show items
						toggleMenuItems('up');
		});
	},
	/*
	on window resize, ajust the bg image dimentions,
	and recalculate the menus width
	*/
	initWindowEvent		= function() {
		/* on window resize set the width for the menu */
		$(window).bind('resize.Menu' , function(e) {
			adjustImageSize($ac_bgimage);
			/* calculate new width for the menu */
			var new_w	= $(window).width() - $title.outerWidth(true);
			$menu.css('width', new_w + 'px');
		});
	},
	/* makes an image "fullscreen" and centered */
	adjustImageSize		= function($img) {
		var w_w	= $(window).width(),
		w_h	= $(window).height(),
		r_w	= w_h / w_w,
		i_w	= $img.width(),
		i_h	= $img.height(),
		r_i	= i_h / i_w,
		new_w,new_h,
		new_left,new_top;
			
		if(r_w > r_i){
			new_h	= w_h;
			new_w	= w_h / r_i;
		}
		else{
			new_h	= w_w * r_i;
			new_w	= w_w;
		}
			
		$img.css({
			width	: new_w + 'px',
			height	: new_h + 'px',
			left	: (w_w - new_w) / 2 + 'px',
			top		: (w_h - new_h) / 2 + 'px'
		});
	},
	/* preloads a set of images */
	loadImages			= function() {
		return $.Deferred(
		function(dfd) {
			var total_images 	= $ItemImages.length,
			loaded			= 0;
			for(var i = 0; i < total_images; ++i){
				$('<img/>').load(function() {
					++loaded;
					if(loaded === total_images)
						dfd.resolve();
				}).attr('src' , $ItemImages[i]);
			}
		}
	).promise();
	};
		
	return {
		init : init
	};
})();

/*
call the init method of Menu
 */
Menu.init();

And that’s all! I hope you enjoyed the tutorial and find it useful!

Manoela Ilic

Manoela is the main tinkerer at Codrops. With a background in coding and passion for all things design, she creates web experiments and keeps frontend professionals informed about the latest trends.

Stay in the loop: Get your dose of frontend twice a week

👾 Hey! Looking for the latest in frontend? Twice a week, we'll deliver the freshest frontend news, website inspo, cool code demos, videos and UI animations right to your inbox.

Zero fluff, all quality, to make your Mondays and Thursdays more creative!

Feedback 178

Comments are closed.
  1. Hi, congratulations for your work!
    One question though : do you always have to return the init variable at the end of the function? otherwise it doesn’t make the trick the second time? I saw this ‘return init’ in another script also.

    Thanks for your answer!

    Maxime

  2. The background image used in menu doesn’t play nice in IE — any advice?

  3. The background image “not playing nice” in IE8 is due to the dimension (minimum allowed) . The image (bg_menu.png) is presently 1 pixel x 1 pixel. IE requires at least a 4 x 4 pixel image.

  4. Thanks, Mary Lou, for another great tutorial! I’ve got a question: How do you do the “Demo” button? I’ve got a Blogger blog and can’t figure out how to do something similar for my tutorials. Also, how do you do the “Download” button?

  5. Thank’s! great tutorial! how can I add a logo (image) instead of the name of the place?? How can i add an image inside the sub menu? Why when i click X (close), the bg not change in to default? how can i fix that? I’m sorry for my english, but i’m italian! THANK’S

  6. Great tutorial, as usual!
    I would like some tips about a similar animation :
    How could you insert some content in the “menu”container (ac_menu) and change it from the navigaion items?

    To be clear, here’s an example:
    http://www.jochemgugelot.nl/

  7. Hello Mary Lou,
    how do I add a hyperlink in the text inside . ac_subitem { ?
    Thanks!

  8. Mary lou: Hi Toine,
    it looks awesome! Your link elements don’t appear because of the style that I’ve set for “.ac_menu > ul > li a”. Just go to line 84 in the style.css and change
    .ac_menu > ul > li a
    to
    .ac_menu > ul > li > a
    You see, like that we will not address all the “a” elements and your links inside of the content boxes will look “normal”
    Hope it helps,
    cheers,
    ML

  9. Hello Mary Lou,
    how do I add a hyperlink in the text inside . ac_subitem { ?
    Thanks!

  10. Congratulations, beautiful work.
    I’m doing some tests, tried to put the colorbox to work together due to limitation of resolution of some screens, but not getting success. Can you tell me if there is any way to get the two work together? I’ve tried the no conflict, but got no success.

    And once again, congratulations!

  11. Hi MaryLou,

    You are my hero! Thank you and the Codrops team so much for all you do!

    I am working on a rebuild of a site for a restaurant, and was going to use this as a base. When I test it on my computer, it works great. However, when I upload it to there server to test, my lightbox & panelscoller don’t work. Can anyone please tell me what I might be missing?

    The site is in preliminary construction phases right now…but you can take a look at it here:
    http://www.saffronsrestaurant.com/saffrons2011/index01.html

    Thanks again CoDrops! And thanks for anyone who can help me!

  12. Bonjour Mary-Lou,
    Thanks for this wonderful work, it’s simply what I was searching since a looooong time for my personal photo site!
    As I am a newbie in web programing, I ask myself (and your’s now !) how it is possible to make a hyperlink in the primary menu that don’t open a box, but just open a different windows…
    If you have the answer, tell me…
    And once again: thanks !

  13. Nicolas, have you tried making your links open as _blank?

    Also, for anyone else who might be having problems getting alightbox gallery to work, try creating an external link for your jquery library…instead of pulling from “js/jquery.lighbox-0.5.min.js”, use the actual http link (so it would be src=”http://www.yourssitehere.com/js/jquery.lightbox-05.min.js”

    Hope this helps! Cheers 🙂

    Hope this helps! Cheers

  14. Thanks Grafixdiva for your help, but reading my own post, I understood that I mismatch… I said “a different window”, but I was talking about a different page. My idea is to click on a link of the horizontal menu that opens a new single page. That’s why I don’t need to open a “box” in the index page…
    I am very confuse about my wrong question. Maybe, if you consider that I’m french, you will forgive me and my damned english !
    Thanks again !

  15. Kerim, it works fine in Safari version 5.0.4 on Windows. Which version do you have?
    Cheers, ML

  16. hye Mary Lou,

    Thanks for this amazing tutorial. however it’s not working in IE. do you have any idea to fix this prob? hope u have a solution for this. thanks again 🙂

  17. @Allie it works fine in IE9. What exactly is not working for you and which version of IE are you using?
    Cheers,
    ML

  18. thanks ML, im using IE7, should be no prob rite? i think my code got mix up already. here i share my URL, so that u can have a look at my prob. sorry to trouble u, but it would be great if u could help! 🙂 thanks!!

  19. hello, I wanted to know how to enter the main menu of links that take you to another page? no sub menu..tank you

  20. Hey i have a question.
    I want just to use the menu slide in, so whitout the images loading first.Because i want to make site whit multiple pages. What part of the code i need to leave away?

    Thanks

  21. Hi Mary Lou,
    great template! I have a question…
    Is possible to change the height of “ac_subitem” indipendently?
    I need to modify size in relation to every single content.

    Tnx

  22. Wonderful tutorial, Mary!

    There’s only one thing that doesn’t work properly (in my Firefox 4 on Windows): If I slightly increase the font size of my browser (‘CTRL’ + ‘+’ instead of ‘CTRL’ + ‘0’) the menu’s width is 1 or more pixels too big so it appears unter the h1 element.

  23. Hi Mary!!…you are a great web designer…but one thing i want to ask you about this design, how i can add jquery scroll panel on the content box, i have content that expand more than the height of the box…

    i’m already try to add jscrollpanel function but it didn’t work…

  24. mary!! i already see where i’m going wrong…now i can add the scrollpanel..

    great tutorial !!!

  25. Hello, I followed your tutorial, however I would add sub??-categories. I managed to modify the CSS to add links, but I would like these links can be displayed the same way as menu items. Is that possible?

  26. I am having difficulty getting this to function properly in IE6. Any tips?

  27. Mary, after following the discussion i have a question, can i have a link in the footer that display the same effect as the ac_menu, do i have to make different function, or just modify the jquery function for the ac_menu?

    please give me a hint or tutorial…

    Thx Mary Lou…

  28. I too am having issues getting lightbox / fancybox to work within this tutorial. . .
    Any tips from anyone would be extremely helpful!

    Cheers otherwise to an awesome tutorial!!!!

    – designer jim

  29. Welcome.
    I have a problem. How to make a scroll?
    I tried different ways but something is blocking.

  30. Hi, beautiful tutorial, but if I need that the menu remains fixed in his position without the effect? I see that i can move easily the box of text content! i like very much the animation and the resizing of the image! thank you, and sorry for my english i hope you can understand it!

  31. Hi Mary,

    Thanks so much, this is fantastic! I have the same question as Sam above though – can you recommend which part of the jQuery to change in order to have the menu slide in, but not load the background images? I’d like to have multiple pages in my site, so the home page just needs a with each linking to a new page, instead of triggering the submenu. Any help? Thanks!

  32. Sorry, should say the home page needs a ul with each li linking to a new page.

  33. Love this! But when I add a link to an image or text in the submenus the link isn’t visible?

    Any help much appreciated!

    Hannah

  34. Hi there,
    I don’t get it I ve followed the tut however my screen is dark with just the first tab and link to Apetizers. the background does not show anymore and I am not sure where to place the last piece of java?

    Here is a sample:

    Untitled Document

    Cafe + BarDhalia

    Appetizers

    Appetizers

    A wonderful serenity has taken possession
    of my entire soul, like these sweet mornings
    of spring which I enjoy with my whole heart.

    Lobster Bisque
    Smoked Salmon Terrine
    Tuna Ceviche
    Wild Mushroom Flan
    Almond Bruschetta
    Green Chilli Canapee
    Artichoke Rucula Salad


    Thanks a lot,

    Manny

  35. Hi everybody; first of all i really love this webpage, very clean, very sober and very ajax.

    I am wanted to know something about 2 points: the first one how keep the menu visible even if you click and show the submenu?

    Second question: how could u change submenu by clicking on each element on the menu and not with the cross ac closer?

    Thanks in advance
    Regards
    Rix

  36. last question sorry , is it possible when you visit the webpage that a submenu appear automaticly?

    Thanks

  37. One of the greatest tutorial.
    Can anyone help me.

    I need a tutorial in with when i click on menu button the background div slide or changes.

    in simple words menu link with background div.

    Thank you.