Expanding Image Menu with jQuery

In today’s tutorial we will create an expanding image menu with jQuery. The idea is to have some columns with black and white image slices that will make a content […]

In today’s tutorial we will create an expanding image menu with jQuery. The idea is to have some columns with black and white image slices that will make a content area slide out when we click on them. We will also slide in the colored version of the image, creating a neat effect.

The photography is by talented Robert Bejil, check out his awesome photos on his Flickr photostream.

So, let’s get started!

The Markup

The HTML structure consists of a main container and an unordered list where each item is one of the columns. We will give the main container the class “ei_menu” and also the id “ei_menu”. The list items are going to contain a link element with two spans inside and a div element with the content. The two spans are for the background image shown at the beginning and the colored version of the image for when we click on the item. Since we are using just one image for each we will have to define the background position. We will take care of that with the classes “pos1” to “pos5” that we will give to the parent link element.

<div id="ei_menu" class="ei_menu">
	<ul>
		<li>
			<a href="#" class="pos1">
				<span class="ei_preview"></span>
				<span class="ei_image"></span>
			</a>
			<div class="ei_descr">
				<h2>Gary</h2>
				<h3>Vocals</h3>
				<p>Some text here...</p>
			</div>
		</li>
		<li> ... </li>
	</ul>
</div>

Let’s take a look at the style.

The CSS

We are going to stretch the container for the list over the page and hide the overflow. Since we will adjust the size of the ul, we don’t want anything sticking out because of the slide out effect, to affect our container:

.ei_menu{
	background:#111;
	width:100%;
	overflow:hidden;
}

We will give enough width to the ul so that the li elements which will be floating, don’t wrap to the next “line” when they expand:

.ei_menu ul{
	height:350px;
	margin-left:50px;
	position:relative;
	display:block;
	width:1300px;
}

The overflow of the li elements is going to be hidden as well because our content inside is actually much bigger. And we only want to show that content, once we expand the while thing by increasing the width of the li:

.ei_menu ul li{
	float:left;
	width:75px;
	height:350px;
	position:relative;
	overflow:hidden;
	border-right:2px solid #111;
}

The “ei_preview” span will contain the black and white background image and will be of absolute positioning:

.ei_preview{
	width:75px;
	height:350px;
	cursor:pointer;
	position:absolute;
	top:0px;
	left:0px;
	background:transparent url(../images/bw.jpg) no-repeat top left;
}

The “ei_image” span will have the colored background image and be almost transparent. We will animate its position and also its opacity to create a cool effect:

.ei_image{
	position:absolute;
	left:75px;
	top:0px;
	width:75px;
	height:350px;
	opacity:0.2;
	background:transparent url(../images/color.jpg) no-repeat top left;
}

To define the positioning of the background for both spans, we will use the following classes, that we give to the parent link element:

.pos1 span{
	background-position:0px 0px;
}
.pos2 span{
	background-position:-75px 0px;
}
.pos3 span{
	background-position:-152px 0px;
}
.pos4 span{
	background-position:-227px 0px;
}
.pos5 span{
	background-position:-302px 0px;
}
.pos6 span{
	background-position:-377px 0px;
}

The content div will also have an absolute position and the left value is going to be the width of the spans:

.ei_descr{
	position:absolute;
	width:278px;
	height:310px;
	border-right:7px solid #f0f0f0;
	padding:20px;
	left:75px;
	top:0px;
	background:#fff;
}

We’ll spice up the heading of the content area with a nice grungy font that we’ll get from the Google fonts. We’ll also add some neat background stripes:

.ei_descr h2{
	font-family: 'Rock Salt', arial, serif;
	font-size:26px;
	color:#333;
	padding:10px;
	text-shadow:0px 0px 1px #fff;
	background:#fff url(../images/stripe_light.gif) repeat top left;
}

The sub heading will also have a font from the collection of Google fonts:

.ei_descr h3{
	font-family: 'Raleway', arial, serif;
	color:#fff;
	text-shadow:0px 0px 1px #000;
	font-style:normal;
	padding:10px;
	background:#333;
}

And finally, we style the paragraphs:

.ei_descr p{
	color:#000;
	padding:10px 5px 0px 5px;
	line-height:18px;
	font-size:11px;
	font-family: Arial, sans-serif;
	text-transform:uppercase;
}

And that’s all the style!
Don’t forget to include the fonts that you are using from Google:

<link href='http://fonts.googleapis.com/css?family=Rock+Salt' rel='stylesheet' type='text/css' />
<link href='http://fonts.googleapis.com/css?family=Raleway:100' rel='stylesheet' type='text/css' />

This is added to the head of your HTML file.
And now, let’s spice it up and add the magic!

The JavaScript

The main idea is to expand a menu item when clicking on it. This means that we want to animate the width of the li to 400 pixel (which is its initial width and the width of the spans, 75 pixel, plus the width of the content area which is 325 pixel counting with the padding and the border). While we do that, we also want to slide in the second span which has the colored background image and animate its opacity. We also want to decrease the opacity of the other items in order to enhance the current one. This will look as if they get darkened because we have a dark background.

Let’s begin by caching some elements:

var $menu				= $('#ei_menu > ul'),
	$menuItems			= $menu.children('li'),
	$menuItemsImgWrapper= $menuItems.children('a'),
	$menuItemsPreview	= $menuItemsImgWrapper.children('.ei_preview'),
	totalMenuItems		= $menuItems.length,

And let’s define the functionality:

ExpandingMenu 	= (function(){
	/*
		@current
		set it to the index of the element you want to be opened by default,
		or -1 if you want the menu to be closed initially
	 */
	var current				= -1,
	/*
		@anim
		if you want the default opened item to animate initially, set this to true
	 */
	anim				= true,
	/*
		checks if the current value is valid -
		between 0 and the number of items
	 */
	validCurrent		= function() {
		return (current >= 0 && current < totalMenuItems);
	},
	init				= function() {
			/* show default item if current is set to a valid index */
		if(validCurrent())
			configureMenu();
			
		initEventsHandler();
	},
	configureMenu		= function() {
			/* get the item for the current */
		var $item	= $menuItems.eq(current);
			/* if anim is true, slide out the item */
		if(anim)
			slideOutItem($item, true, 900, 'easeInQuint');
		else{
				/* if not, just show it */
			$item.css({width : '400px'})
			.find('.ei_image')
			.css({left:'0px', opacity:1});
					 
				/* decrease the opacity of the others */	
				$menuItems.not($item)
						  .children('.ei_preview')
						  .css({opacity:0.2});	 
		}
	},
	initEventsHandler	= function() {
			/*
			when we click an item the following can happen:
			1) The item is already opened - close it!
			2) The item is closed - open it! (if another one is opened, close that one!)
			*/
		$menuItemsImgWrapper.bind('click.ExpandingMenu', function(e) {
			var $this 	= $(this).parent(),
			idx		= $this.index();
				
			if(current === idx) {
				slideOutItem($menuItems.eq(current), false, 1500, 'easeOutQuint', true);
				current = -1;
			}
			else{
				if(validCurrent() && current !== idx)
						slideOutItem($menuItems.eq(current), false, 250, 'jswing');
					
				current	= idx;
					slideOutItem($this, true, 250, 'jswing');
			}
			return false;
		});
	},
		/* if you want to trigger the action to open a specific item */
		openItem			= function(idx) {
			$menuItemsImgWrapper.eq(idx).click();
		},
		/*
		opens or closes an item
		note that "mLeave" is just true when all the items close,
		in which case we want that all of them get opacity 1 again.
		"dir" tells us if we are opening or closing an item (true | false)
		*/
	slideOutItem		= function($item, dir, speed, easing, mLeave) {
		var $ei_image	= $item.find('.ei_image'),
				
		itemParam	= (dir) ? {width : '400px'} : {width : '75px'},
		imageParam	= (dir) ? {left : '0px'} : {left : '75px'};
			
			/*
			if opening, we animate the opacity of all the elements to 0.1.
			we do this in order to enhance the opened item more:
			*/
		if(dir)
		/*
				alternative:
				$menuItemsPreview.not($menuItemsPreview.eq(current))
								 .stop()
								 .animate({opacity:0.1}, 500);
		 */
			$menuItemsPreview.stop()
		.animate({opacity:0.1}, 1000);
		else if(mLeave)
			$menuItemsPreview.stop()
		.animate({opacity:1}, 1500);
			
			/* the <li> expands or collapses */
		$item.stop().animate(itemParam, speed, easing);
			/* the image span (color) slides in or out */
		$ei_image.stop().animate(imageParam, speed, easing, function() {
				/* 
				if opening, we animate the opacity to 1, 
				otherwise we reset it.
				*/
			if(dir)
				$ei_image.animate({opacity:1}, 2000);
			else
				$ei_image.css('opacity', 0.2);
		});
	};
		
	return {
		init 		: init,
		openItem	: openItem
	};
})();
	
/*
call the init method of ExpandingMenu
 */
ExpandingMenu.init();

/*
if you want to open / close a specific item you could do it like this:
ExpandingMenu.openItem(3); // toggles item 3 (zero-based indexing)
*/

And that’s all!

Alternative Demos

You can check out the following other demos which all make use of the different options in our script:

All of these versions will be included in the ZIP file.
We hope you enjoyed the 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 50

Comments are closed.
  1. Just AMAZING !!
    i love the transitions from the grayscale to the color.
    i think ill use this concept for my next project for a uniqe contacts list

  2. Good job, nice one, again… 😉

    For saying something, to my mind, the image “span (color) slides” is not necessary. A simple fade In/Out effect’d be better.

    Best regards from france.

  3. one more time, fantastic !!!!
    lovely effect, you pushed and pushed with new and fresh jquery tutorials, we love it, thanks for share them and congratulations for all the work involved !!

  4. MARY LOU i love u . u r greet . u solve my all problem in web. thanks thanks thanks

  5. There are times I think this kind of tutorial was made by a genius. Can’t believe its you. Very detail and straight-forward with awesome result.

    This can be made as a horizontal accordion too.

  6. Damn… these tutorials are just too sexy. You see, first just the HTML/CSS design is sexy, then it gets even sexier with the animations. Great work on this one, and all the others on the site.

    You are a great inspiration!

  7. Hey Mary Lou! I was curious, is it possible to embed this in a carousel/gallery?

    Thanks!

  8. Good article and smart desing…
    Mary Lou thanks for Jack Johnson,

    please share some times good songs which you listen most 😉

  9. Wow, what a great and smooth transitions effects dude!

    Specially the images transitions in different modes.. This is just WOW..!!

    You Rock!

  10. estube siguiendo codrops desde hace un tiempo y puedo decirles que tienen los mejores tutoriales que he visto.

    gracias codrops por todos sus aportes.

    sigan adelante… 😉

  11. Working a site for a hair salon, I promised to give them something “Sexy”….the owner smirked, “How sexy can a website be?” After using a few of your tut’s, I showed him. So so beautiful! YOU ARE MY HERO!!!!

  12. Hello. Awesome work. I definately learned a lot messing around with this function!

    I have a question. Is there a way to get the slides to play automatic rather than being still?

    Thanks!

  13. Could you give some information on how to make it a vertical menu?
    Should I change the item widths?
    Thanks in advance

  14. Incredible, thanks so much!

    I changed it to trigger by mouseover instead of click. Any way to make them all collapse when mouse is off menu?

  15. How can I highlight current item in trigger menu? Like click David button and it gets different background color.

  16. great tutorial,
    how would you get rid of the opacity and just have the colored images

  17. wonder if it’d be possible to put this inside of a centered container instead of left of page ?

  18. I was wondering if someone know how to make working with a mOver/mLeave ?
    thanks in advance !

  19. Awesome tutorial, please also post tutorial on how to apply this jQuery functions to WordPress-powered blog, thank you 🙂

  20. suberb looking menu.

    i’ve seen it before under and i was very impressed.

    thank you for sharing,
    Helmuts

  21. Great menu – If only I could figure out how to get it to advance through the slides automatically..
    Any help appreciated!

  22. Seriously awesome code going on here! I’ve been looking for options to replace Flash (blah!) on the company website and I think you’ve answered my prayers!

  23. hi this is great, but the javascript is not valid when I tried to valid them … the error is at the &, when I tried to replace with & still error and the syntax error on my dreamweaver…any help appreciate

  24. Sorry if I seems newbie, but I don’t speack english and don’t understand Javascript very well but…

    I can’t change the width of boxes where text is… I try to enlarge .ei_descr but the content of the box is overflow and I can’t read what is write in…

    Very sorry if isn’t the place for ask question, but the menu is just perfect and I need to use it…

    Thanks very much for answers. ?
    Hope it will not bother.
    Thanks.

  25. Hello,

    I love this slider and would like to use it on a client’s website. Could you help me figure out how to integrate it into a WordPress site?

    Thanks!

  26. This tutorial is awesome!
    I just have a question: when I try to validate the html code at W3c, its give me this errors with “&”:
    – character “&” is the first character of a delimiter but occurred as data –

    How can i solve this?

    Thank you so much!