Collapsing Site Navigation with jQuery

Today we will create a collapsing menu that contains vertical navigation bars and a slide out content area. When hovering over a menu item, an image slides down from the […]

Today we will create a collapsing menu that contains vertical navigation bars and a slide out content area. When hovering over a menu item, an image slides down from the top and a submenu slides up from the bottom. Clicking on one of the submenu items will make the whole menu collapse like a card deck and the respective content area will slide out.

The beautiful fashion photos are taken from Beyrouth’s photostream on flickr. The specific set can be found here.

So, let’s get started!

The Markup

Our HTML will consist of a main container with the class and id “cc_menu”. Here we will place all our vertical menu items and the main content div:

<div id="cc_menu" class="cc_menu">
	<div class="cc_item" style="z-index:5;">
		<img src="images/1.jpg" alt="image" />
		<span class="cc_title">Collection</span>
		<div class="cc_submenu">
			<ul>
				<li class="cc_content_1">Winter 2010</li>
				<li class="cc_content_2">Spring 2011</li>
			</ul>
		</div>
	</div>
	<div class="cc_item" style="z-index:4;">
		<img src="images/2.jpg" alt="image" />
		<span class="cc_title">Stores</span>
		<div class="cc_submenu">
			<ul>
				<li class="cc_content_3">Milano</li>
				<li class="cc_content_4">Paris</li>
			</ul>
		</div>
	</div>
	...
	<div id="cc_content" class="cc_content">
		<span id="cc_back" class="cc_back"><< Go back</span>
		<div class="cc_content_1">
			<h1>Winter 2010</h1>
			<p>Some content</p>
		</div>
		<div class="cc_content_2">
			<h1>Spring 2011</h1>
			<p>Some content</p>
		</div>
		...
	</div>
</div>

The first item will get a z-index of 5 and then we will decrease the z-index for the next items. This will make the last item be in the lowest layer. We do this in order to create the card deck collapsing effect.

Each submenu item will share its class with the respective content div. Like that we can make the right content div appear whenever we click on a submenu item.

Let’s take a look at the styling.

The CSS

Our main div that surrounds everything will have the following style:

.cc_menu{
	width:700px; /*140px * 5*/
	height:600px;
	position:relative;
	font-size:14px;
	text-transform:uppercase;
	color:#fff;
}

The width of this container is the sum of all the item widths which is 5 times 140 pixels.

The style of each navigation item will be the following:

.cc_item{
	text-align:center;
	width:140px;
	height:600px;
	float:left;
	border-bottom:1px solid #000;
	background:#444 url(../images/bg.png) repeat top left;
	position:relative;
	-moz-box-shadow:3px -3px 10px #000;
	-webkit-box-shadow:3px -3px 10px #000;
	box-shadow:3px -3px 10px #000;
}

We will give each item a box shadow so that the layering becomes visible. (That does not work in IE yet, so you will have to add a border or some other background image that enhances this.)

The title in each menu element will have a dark background with a subtle shadow around:

span.cc_title{
	color:#fff;
	font-size:16px;
	top:200px;
	left:5px;
	position:absolute;
	padding:3px 0px;
	background:#111;
	width:130px;
	display:block;
	z-index:11;
	-moz-box-shadow:1px 1px 4px #000;
	-webkit-box-shadow:1px 1px 4px #000;
	box-shadow:1px 1px 4px #000;
}

The submenu list will be styled as follows:

.cc_submenu ul{
	list-style:none;
	width:140px;
	margin:0;
	padding:0;
	height:0px; /*increase to 200px to slide up*/
	overflow:hidden;
	text-align:left;
	background:#000;
	position:absolute;
	left:0px;
	bottom:0px;
	opacity:0.7;
	z-index:13;
}

We will position the list at the bottom of the item and give it a height of 0 pixel. We will then increase the height to 200 pixels for it to slide up from the bottom.

The list elements of the submenu list will have the following style:

.cc_submenu ul li{
	color:#ddd;
	cursor:pointer;
	padding:10px;
}

The image that will slide in from the top will be positioned negatively, meaning that we will hide it at the top of the item and the page:

.cc_item img{
	position:absolute;
	width:140px;
	height:600px;
	top:-600px;
	left:0px;
}

In our JavaScript function we will make it slide down from the top by animating the top value to 0px.

The div that surrounds all the contents will also be hidden to the left of the page by setting the left value to -700 pixels:

.cc_content{
	width:600px;
	height:600px;
	border-bottom:1px solid #000;
	position:absolute;
	left:-700px;
	background:#444 url(../images/bg.png) repeat top left;
	overflow:hidden;
	-moz-box-shadow:4px 0 7px #000;
	-webkit-box-shadow:4px 0 7px #000;
	box-shadow:4px 0 7px #000;
}

The single content divs will have the following style:

.cc_content div{
	display:none;
	margin:20px;
}

We will give each paragraph the following style:

.cc_content p{
	background:#000;
	padding:20px;
	opacity:0.7;
}

And finally, we will position the back span at the bottom right of the content container:

span.cc_back{
	position:absolute;
	bottom:10px;
	right:10px;
	cursor:pointer;
	color:#ddd;
}

And that was the style. Now, let’s take a look at the JavaScript magic.

The JavaScript

We will have several function taking care of the behavior of our navigation. Whenever we hover a menu item, we want the image to slide in from the top and the submenu to slide in from the bottom. And, of course, when we leave an item, we want the reverse to happen. The functions m_enter and m_leave take care of that behavior.
The function fold will make the menu collapse once a submenu item is clicked. The initial position is recovered by the function unfold.
The two functions showContent and hideContent take care of the respective content appearing and disappearing.

In our main jQuery function we will start by defining some variables:

//all the menu items
var $items 		= $('#cc_menu .cc_item');
//number of menu items
var cnt_items	= $items.length;
//if menu is expanded then folded is true
var folded		= false;
//timeout to trigger the mouseenter event on the menu items
var menu_time;

Now we will bind the mouseenter and the mouseleave to each item. We will also bind the click event to list elements in the submenu:

$items.unbind('mouseenter')
	  .bind('mouseenter',m_enter)
	  .unbind('mouseleave')
	  .bind('mouseleave',m_leave)
	  .find('.cc_submenu > ul > li')
	  .bind('click',function(){
	var $li_e = $(this);
		  //if the menu is already folded,
		  //just replace the content
	if(folded){
		hideContent();
		showContent($li_e.attr('class'));
	}
		  else //fold and show the content
		fold($li_e);
});

In the following we will define the m_enter function:

function m_enter(){
	var $this 	= $(this);
	clearTimeout(menu_time);
	menu_time 	= setTimeout(function(){
	//img
	$this.find('img').stop().animate({'top':'0px'},400);
	//cc_submenu ul
	$this.find('.cc_submenu > ul').stop().animate({'height':'200px'},400);
	},200);
}

The timeout is used to prevent this event to trigger if the user moves the mouse with a considerable speed through the menu items.

The m_leave function is defined as follows:

function m_leave(){
	var $this = $(this);
	clearTimeout(menu_time);
	//img
	$this.find('img').stop().animate({'top':'-600px'},400);
	//cc_submenu ul
	$this.find('.cc_submenu > ul').stop().animate({'height':'0px'},400);
}

When clicking on the back span, we want the unfold function to trigger:

$('#cc_back').bind('click',unfold);

The fold function will show only the menu column of the chosen submenu and make all the other items animate to the left by setting the margin to -140 pixels:

function fold($li_e){
	var $item		= $li_e.closest('.cc_item');

	var d = 100;
	var step = 0;
	$items.unbind('mouseenter mouseleave');
	$items.not($item).each(function(){
		var $item = $(this);
		$item.stop().animate({
			'marginLeft':'-140px'
		},d += 200,function(){
			++step;
			if(step == cnt_items-1){
				folded = true;
				showContent($li_e.attr('class'));
			}
		});
	});
}

The unfold function shows all the menu items and also hides any item’s image and submenu that might be displayed:

function unfold(){
	$('#cc_content').stop().animate({'left':'-700px'},600,function(){
		var d = 100;
		var step = 0;
	$items.each(function(){
			var $item = $(this);

			$item.find('img')
				 .stop()
				 .animate({'top':'-600px'},200)
				 .andSelf()
				 .find('.cc_submenu > ul')
				 .stop()
				 .animate({'height':'0px'},200);

			$item.stop().animate({
			'marginLeft':'0px'
			},d += 200,function(){
				++step;
				if(step == cnt_items-1){
					folded = false;
					$items.unbind('mouseenter')
						  .bind('mouseenter',m_enter)
						  .unbind('mouseleave')
						  .bind('mouseleave',m_leave);

					hideContent();
				}
			});
		});
	});
}

The function to show the content will animate our content container to the right by setting the left value to 140 pixels. It will also fade in the respective content:

function showContent(idx){
	$('#cc_content').stop().animate({'left':'140px'},200,function(){
		$(this).find('.'+idx).fadeIn();
	});
}

And finally, the function to hide the content:

function hideContent(){
	$('#cc_content').find('div').hide();
}

To cufonize the font in our menu, we will simply add these lines to the head of our HTML:

<script src="js/cufon-yui.js" type="text/javascript"></script>
<script src="js/Liberation_Sans.font.js" type="text/javascript"></script>
<script type="text/javascript">
	Cufon.replace('span');
	Cufon.replace('li');
	Cufon.replace('h1');
	Cufon.replace('p');
</script>

We will be using the font Liberation Sans which you can also find here.

And that’s all! We hope you enjoyed this tutorial and find it useful!

Tagged with:

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 up to date with the latest web design and development news and relevant updates from Codrops.

Feedback 124

Comments are closed.
  1. hi everyone!

    I been trying to edit this navigation to work the way I want it to for some time, and starting to get most of it to work. I see that alot people here have had many of the same troubles I’ve had, so I figured I could help you people out. bare in mind that I’m actually a total noob with javascript! but I good at searching the net for info, and just trying out things. so my sulotions may not be the best, but they seem to get the job done.

    what I got fixed so far:
    – menu centered
    – space in between the sliders
    – links from section to section inside content area

    working on:
    – getting the whole item to work as a link
    – control over the submenu animation
    – ++ some other stuff hehe

    I’ll be posting the stuff that I got working so far, and if anyone know how to fix the other things I’m working on that would be great!

  2. – how to center the navigator –

    this is quite easy. first we open the index.html and make a new div that surrounds the whole menu:


    Get in touch
    Winter collection

    now we need to center the menuContainer div. open style.css and add this:

    #menuContainer {
    width: 700px; /* same as .cc_menu width */
    position: relative;
    margin: 0 auto;
    }

    while we’re in style.css we need to make some changes to the cc_menu div aswell:

    .cc_menu{
    width:747px; /* width of .cc_content (600px) + 1*.cc_item (140px) + .cc_content shadow (7px) */
    height:610px; /* height of .cc_item (600px) + + .cc_item shadow (10px) */
    overflow:hidden; /* add this to hide the content when all menu items are shown */
    position:relative;
    font-size:14px;
    text-transform:uppercase;
    color:#fff;
    }

    that’s all. hope that helps the ones strugling with this.

  3. haha, some of the text from my last post was read as html and dissapeared.

    from last post:
    “open the index.html and make a new div that surrounds the whole menu:”

    just make a div with id “menuContainer” surrounding the cc_menu div.

  4. Hello
    this template is fantastic,
    How can i make the keyword change when displaying a part of the site?

    how to make visible for google and so on?
    Thanks

  5. @Kimbaone:
    html/php als utf-8 Datei verwahren, ansonsten spezielle Kodes benutzen
    html/php save as utf-8 File, or else use the special codes

    @donovan / @saya and others:
    after –>
    before –>

    See also http://www.benjaminrinaldi.com for css-file and sourcefile

  6. aah, my html-codes are not shown: ok, I’ll try again…

    after the body code at the top of your page and before the body code at the end of your page use the html-codes CENTER, in order to center everything within your document (as long as there are no other aligning codes…)

  7. beautiful navigation and great tutorial!
    I need to have a sitemap though with links from the sitemap section to the other sections. The links need to be inside the content area.

    Can anyone help me with that?

  8. Hi Mary,
    A great tutorial and code. Congratulations.
    However, I find that it does not work properly on IE8 and I cannot see the text headings and details in this browser. Can you suggest some remedy ?

  9. Hi, good job 🙂
    Why if I insert one before , like this:

    Winter 2010

    Some content

    the text disappears?

  10. got it working!!!! thanks EK. but the font has changed? i want the original font.

    ?
    Thanks in advance

  11. how to make a scrollbar if your “about” > “Educational backgroound” is longer that the window?

    I hope anyone could help. Thanks in advance!

  12. Firstly, thanks for such an amazing tutorial, as with all your tutorials.

    I fell in love with the idea of creating a dynamic single page site for my wife’s cake business upon seeing this and have been feverishly beavering away trying to get it ready as a christmas present for her next week.

    However, I’ve hit a stumbling block and would love some assistance if possible.

    I need to have a scrollable thumbnail gallery display in certain cc-content areas but I have tried implementing nearly all of the available ones on the web to no avail. I’ve set display:block and they all show but do nothing functional.

    Do you have any recommendations as to how I get a gallery of this type working within the cc-content areas?

    Many, many thanks for any help you could give on this!

  13. I found the solution for linking to a different section.

    1. Use to make the link. Give it an ID, and a class.

    2. Go to the CSS and create an entry using cc_back as the basis. For example:

    span.cc_gotodifferentpage{
    position:absolute;
    cursor:pointer;
    color:#ddd;
    }

    3. Go to the Javascript code and add this (this is assuming you’ve given the span id of cc_gotodifferentpage. Change cc_content_9 to whatever section you want to link it to):

    $(‘#cc_gotodifferentpage’)

    .bind(‘click’,function(){

    //if the menu is already folded,
    //just replace the content
    var $li_e = $(‘#cc_menu .cc_item .cc_submenu ul .cc_content_9’);

    //if the menu is already folded,
    //just replace the content
    hideContent();
    specialfold($li_e);

    });

    4. Add the following function (don’t change anything, unless you want to change the name of the function):

    function specialfold($li_e){

    var $item = $li_e.closest(‘.cc_item’);

    var d = 100;
    var step = 0;
    $item.stop().animate({‘marginLeft’:’0px’},d += 0);
    $item.find(‘img’).stop().animate({‘top’:’0px’},400);
    //cc_submenu ul
    $item.find(‘.cc_submenu > ul’).stop().animate({‘height’:’260px’},400);

    $items.unbind(‘mouseenter mouseleave’);
    $items.not($item).each(function(){
    var $item = $(this);
    $item.stop().animate({‘marginLeft’:’-180px’},d += 200,function(){
    ++step;
    if(step == cnt_items-1){
    folded = true;
    showContent($li_e.attr(‘class’));
    }
    });

    });

    }

  14. The website ate up my HTML.

    For 1. I meant:

    1. Use SPAN to make the link. Give it an ID, and a class.

  15. Great tutorial!
    However, i want to center the whole menu. But when i do this the tab “collection” always remains on the page and is not overlapped by the other tabs. Is there a way to solve this?

    thanks!

  16. hi thanks for this great tutorial. im new to web design. i want to add new gallery. so reply me where i have to change in the code

  17. How do ls that images are always visible in the menu and the animation is the same as mouseover?

    Cordially

    FDG

  18. I repeat my question more clearly : how to modify the code so that images are always visible, and the animation is still operational.

    Cordially

    FDG