Beautiful Background Image Navigation with jQuery

In this tutorial we are going to create a beautiful navigation that has a background image slide effect. The main idea is to have three list items that contain the […]

In this tutorial we are going to create a beautiful navigation that has a background image slide effect. The main idea is to have three list items that contain the same background image but with a different position. The background image for each item will be animated to slide into place in different times, creating a really nice effect. The background image sliding direction from the list item in the middle will depend on which item the user was before: coming from the right, it will slide from the left and vice versa.

On top of that we will have sub-menus that appear with their semi-transparent background sliding in. These backgrounds create an awesome effect of actually just being one element that slides into place, changing its color.

Note: There is a new version which let’s you customize things better:
Sliding Background Image Menu with jQuery

We will be using the amazing Background-Position Animation Plugin by Alexander Farkas.

The photos that we will be using are from Pat’s beautiful B&W collection on Flickr.

There will be a little bit of CSS3 involved which absence will almost not be notable when using a browser that does not support its properties (like IE).

We tried to make this one cross-browser compatible and voilà! It works beautifully in Google Chrome, Firefox, Opera, Safari, IE8, IE7 and guess what, it even works respectively in IE6. (We are using an adapted style sheet and you will have to apply some PNG fix if you want the sub-menu backgrounds to be  semi-transparent. )

OK, so let’s get started!

The Markup

The HTML will consist of a wrapper div and an unordered list with three list items. We will initially set some “bg” classes that will have the respective background images. The “menuWrapper” will always have the “bg” class of the current list item so that we have the background image of the current list item.

<div id="menuWrapper" class="menuWrapper bg1">
	<ul class="menu" id="menu">
		<li class="bg1" style="background-position:0 0;">
			<a id="bg1" href="#">Our Passion</a>
			<ul class="sub1" style="background-position:0 0;">
				<li><a href="#">Submenu 1</a></li>
				<li><a href="#">Submenu 2</a></li>
				<li><a href="#">Submenu 3</a></li>
			</ul>
		</li>
		<li class="bg1" style="background-position:-266px 0px;">
			<a id="bg2" href="#">Our Brands</a>
			<ul class="sub2" style="background-position:-266px 0;">
				<li><a href="#">Submenu 1</a></li>
				<li><a href="#">Submenu 2</a></li>
				<li><a href="#">Submenu 3</a></li>
			</ul>
		</li>
		<li class="last bg1" style="background-position:-532px 0px;">
			<a id="bg3" href="#">Contact</a>
			<ul class="sub3" style="background-position:-266px 0;">
				<li><a href="#">Submenu 1</a></li>
				<li><a href="#">Submenu 2</a></li>
				<li><a href="#">Submenu 3</a></li>
			</ul>
		</li>
	</ul>
</div>

For the background animation plugin to work correctly we need to set the position of the background image as inline styles initially. We already set the right position for each background image, for example, the third item will have the background image positioned to the outer right.

As you can see, every list item has another sub-list with three more items. The background image position of these sublists is set to a value that hides the image. When we animate the background then, we will create the effect of sliding it in from the left or the right.

The CSS

Let’s start by the general style of the the “menuWrapper” which will contain the font styles and the size of the whole element:

.menuWrapper{
    font-family: "Trebuchet MS", Arial, sans-serif;;
    font-size: 15px;
    font-style: normal;
    font-weight: normal;
    text-transform:uppercase;
    letter-spacing: normal;
    line-height: 1.45em;
    position:relative;
    margin:20px auto;
    height:542px;
    width:797px;
    background-position:0 0;
    background-repeat:no-repeat;
    background-color:transparent;
}

The list and the list items of the first layer will have the following style:

ul.menu{
    list-style:none;
    width:797px;
}
ul.menu > li{
    float:left;
    width:265px;
    height:542px;
    border-right:1px solid #777;
    background-repeat:no-repeat;
    background-color:transparent;
}
ul.menu > li.last{
    border:none;
}

The selector “>” addresses only the li elements of the first layer and will not be applied to the ones of the sub-menus. We will give the last li the class “last” in order to remove the right border.

The following three classes define the background images. We will give these classes to all the li elements depending on which one we hover. So, if we hover the second li, we will give the “bg2” class to all the first layer list items:

.bg1{
    background-image: url(../images/1.jpg);
}
.bg2{
    background-image: url(../images/2.jpg);
}
.bg3{
    background-image: url(../images/3.jpg);
}

Let’s define the look of the link elements in the first layer list. Since the li elements have a big height, we need to push the link elements down. We do that by setting a high top margin:

ul.menu > li > a{
    float:left;
    width:265px;
    height:50px;
    margin-top:450px;
    text-align:center;
    line-height:50px;
    color:#ddd;
    background-color:#333;
    letter-spacing:1px;
    cursor:pointer;
    text-decoration:none;
    text-shadow:0px 0px 1px #fff;
}

To center the text of a link element vertically, you can give a line-height value equal to the height of the element. (I don’t know why, but when I started with learning CSS, I automatically always used paddings to adapt the height of the element and to center it. Using line-height was a real delight because you can control sizes much better like that.)

The second layer lists would naturally appear after the first layer list, so we use a negative top margin to “pull” them up:

ul.menu > li ul{
    list-style:none;
    float:left;
    margin-top:-180px;
    width:100%;
    height:110px;
    padding-top:20px;
    background-repeat:no-repeat;
    background-color:transparent;
}

The li elements of the sub-menu will be initially hidden. In the script we will fade them in when we hover over their parent list item link:

ul.menu > li ul li{
    display:none;
}

Now we define the classes for the background images of the sub-menus:

ul.menu > li ul.sub1{
    background-image:url(../images/bg1sub.png);
}
ul.menu > li ul.sub2{
    background-image:url(../images/bg2sub.png);
}
ul.menu > li ul.sub3{
    background-image:url(../images/bg3sub.png);
}

The link elements of the li elements will have the following style:

ul.menu > li ul li a{
    color:#fff;
    text-decoration:none;
    line-height:30px;
    margin-left:20px;
    text-shadow:1px 1px 1px #444;
    font-size:11px;
}

ul.menu > li ul li a:hover{
    border-bottom:1px dotted #fff;
}

The first sub-list will be shown in the beginning:

ul.menu > li ul.sub1 li{
    display:block;
}

And that’s all the style. Take a look at the ZIP file for the IE6 style sheet.
Now let’s take a look at the JavaScript.

The JavaScript

What we do in the script is the following: we first check where we are coming from and then we animate the background positions of the list elements in the first level and the backgrounds of the sub-menus accordingly:

$(function() {
	/* position of the <li> that is currently shown */
	var current = 0;

	$('#bg1,#bg2,#bg3').mouseover(function(e){

		var $this = $(this);
		/* if we hover the current one, then don't do anything */
		if($this.parent().index() == current)
			return;

		/* item is bg1 or bg2 or bg3, depending where we are hovering */
		var item = e.target.id;

		/*
		this is the sub menu overlay. Let's hide the current one
		if we hover the first <li> or if we come from the last one,
		then the overlay should move left -> right,
		otherwise right->left
		 */
		if(item == 'bg1' || current == 2)
			$('#menu .sub'+parseInt(current+1))
			    .stop()
				.animate({backgroundPosition:"(-266px 0)"},300,function(){
					$(this).find('li').hide();
				});
		else
			$('#menu .sub'+parseInt(current+1))
				.stop()
				.animate({backgroundPosition:"(266px 0)"},300,function(){
					$(this).find('li').hide();
				});

		if(item == 'bg1' || current == 2){
			/*
			if we hover the first <li> or if we come from
			the last one, then the images should move left -> right
			*/
			$('#menu > li')
				.animate({backgroundPosition:"(-800px 0)"},0)
				.removeClass('bg1 bg2 bg3')
				.addClass(item);
			move(1,item);
		}
		else{
			/*
			if we hover the first <li> or if we come
			from the last one, then the images should move
			right -> left
			*/
			$('#menu > li')
				.animate({backgroundPosition:"(800px 0)"},0)
				.removeClass('bg1 bg2 bg3')
				.addClass(item);
			move(0,item);
		}

		/*
		We want that if we go from the first one to the last one
		(without hovering the middle one), or from the last one
		to the first one, the middle menu's overlay should also
		slide, either from left to right or right to left.
		 */
		if(current == 2 && item == 'bg1'){
			$('#menu .sub'+parseInt(current))
			.stop()
			.animate({backgroundPosition:"(-266px 0)"},300);
		}
		if(current == 0 && item == 'bg3'){
			$('#menu .sub'+parseInt(current+2))
			.stop()
			.animate({backgroundPosition:"(266px 0)"},300);
		}

		/* change the current element */
		current = $this.parent().index();

		/* let's make the overlay of the current one appear */

		$('#menu .sub'+parseInt(current+1))
			.stop().animate({backgroundPosition:"(0 0)"},300,function(){
				$(this).find('li').fadeIn();
			});
	});
	/*
	dir:1 - move left->right
	dir:0 - move right->left
	 */
	function move(dir,item){
		if(dir){
			$('#bg1').parent()
					 .stop()
					 .animate({backgroundPosition:"(0 0)"},200);
			$('#bg2').parent()
					 .stop()
					 .animate({backgroundPosition:"(-266px 0)"},300);
			$('#bg3').parent()
					 .stop()
					 .animate({backgroundPosition:"(-532px 0)"},400,function(){
						$('#menuWrapper').removeClass('bg1 bg2 bg3')
										 .addClass(item);
					 });
		}
		else{
			$('#bg1').parent()
					 .stop()
					 .animate({backgroundPosition:"(0 0)"},400,function(){
						$('#menuWrapper').removeClass('bg1 bg2 bg3')
										 .addClass(item);
					 });
			$('#bg2').parent()
					 .stop()
					 .animate({backgroundPosition:"(-266px 0)"},300);
			$('#bg3').parent()
					 .stop()
					 .animate({backgroundPosition:"(-532px 0)"},200);
		}
	}
});

And that’s it! A beautiful cross-browser capable effect!
I hope that you enjoyed the tutorial.

Message from TestkingIf want to learn professional web designing but have no time to attent regular class then testking a+ is best solution though. Download the testking mcse design tutorials and testking VCP-410 study guide to learn how to create beautiful and unique logo designs.

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 335

Comments are closed.
  1. @Alan Well, the question is, how would we make the submenu block appear when we hover? It could not slide from another item if you don’t hover over the other item first. So, this is kind of tricky, it would loose the initial idea… Just imagine you hover over the middle item, where would the submenu block appear from?

  2. @Mary lou, yes I can see that the way thinks work at the moment you’ve got to be sliding *from* somewhere. Is there no way though to make it so the first block *is* the default origin but onload that block’s menus (!not the background!) are hidden. I’m sorry i’m just restating the problem realy, I couldn’t see how to do it. The problem is not the background image, that can be the first block. The menus themselves don’t slide so I thought there might be a way. Is that any clearer?
    Thanks again for your help.

  3. ps .. when I say menus i only mean the submenu block with the transparent background (sub1 dans le css) and not the main menu bar at the bottom of the screen (our passion, our brands, etc)

  4. Dear ML, tried adding the brackets, the error showed while debugging in IE 8 also. But no positive result.
    Tried checking the script at http://jsfiddle.net
    There is indeed an ERROR, still wondering where the bug is…?

  5. Has anybody cufonized this wonderful jQuery menu yet? I did, but it’s not working perfect yet. When I mouse over the menu from left to right, or mouse away from menu to return to it = it behaves fine. When I mouse over the menu from right to left, the image background doesn’t load for the menu headings with sub-menus attached to them.
    Thanks for your guidance.

  6. Finally met success in debugging. Now its working in my blog. But the animations are not so smooth. Some problem in CSS as is evident from the picture which has a double edge at the left hand side.
    http://spicemyblog.blogspot.com
    Some body help again…!!!

  7. you need to make sure you have the following in your CSS doc
    *{
    margin:0;
    padding:0;
    }

  8. MaryLou, absolutely love it! Unique and works fantastically!

    Just have one minor glitch that I can’t figure out! I have modified R.Bird’s 4 panel version to make it a 5 panel version… but when going left-to-right the 4th panel opens the wrong way… but going right-to-left it is fine.

    Could I send you (or R.Bird) the file so you could have a quick look over it? Would really appreciate any help!

    Thanks so much for sharing this!

    Greg

  9. Ratnakar: Perhaps also pre-loading images might be a good idea. First hover for me showed a delay whilst images were being loaded …

  10. Hello Mary Lou,
    Great tutorial and brilliant solution 🙂
    Do you mind if I use it for my own personal site? 😀 with great modifications of course 🙂
    Thank you very much for sharing

    • @Cezar Of course I don’t mind! I am glad that you like the solution, and it just looks fantastic with your drawings! Cheers, ML

  11. i wanted to do a four column one, but a bigger dimension, but its not quite working out for me, if you could help me out that would be awesome

  12. i figured it out after all, i was just wondering if you mind me using this for my photography website?

    • @Jayce, of course I don’t mind! Please feel free to use this for your site 🙂 Cheers, ML

  13. @Jayce, hey I checked out you website and the navigation definitely needs that the images are preloaded… Just download the ZIP again, we updated it some time ago. Also, for working with 4 pictures, check out the comments and the link of R.BIRD, I think that will help you a lot! Cheers, ML

  14. i got the 4 column figured out, and i updated the link again. Its just the pre loading that need to add yet. and thank you so much for this, this is exactly what i was wanting my site to look like 🙂

  15. This is oen of the b’ful example i have seen so far here. great tutorial !

  16. Great tut.
    Has anyone managed to get more than 3 images working in Internet Explorer ?

  17. Great work, Mary Lou – thanx!

    I have a problem – I just wanted to try the option with the midlle menu to open first, but there was a problem with your link to the .zip file.

    • @janez, sorry about that, the correct link to that ZIP file can be found here . Thanks for you feedback! Cheers, ML

  18. Love this script! So crisp.

    If I wanted the images to be smaller, say 740×501 instead of 800×542, what would be the best way to adapt this?

    Thanks!

  19. A quick update to my previous post…doing what I would consider “obvious” gives a weird repeat to the image on the right, about 40px wide. So it’s not just a matter of changing the size?

  20. Okay I am using the file that R.Bird was so kind to provide but I only need one sub menu on one of my sections how do I get it to line up flush with the along the main menu. Right now its just floating up at the normal position by itself. I have tried changing CSS and nothing is moving it.

  21. Has anyone tried using this with wordpress. Apparently wordpress uses jquery in safe mode. So I wrapped the javascript code in index.html with this.

    (function($){
    The code that was in index.html
    })(jQuery);

    Still I cannot get it to work.

    My CSS files are linked up properly
    My js files are linked up properly

    Has anyone made this work with wordpress? Any ideas?

    Cheers and awesome script.

  22. Figured out the wordpress installation issue from my last comment.

    Wrapped js code with the following
    (function($){
    The code that was in index.html
    })(jQuery);

    Found this line in index.html
    }).attr(‘src’, ‘images/’+i+’.jpg’);

    changed it to this
    }).attr(‘src’, ‘/images/’+i+’.jpg’);

  23. Comment about line change stripped out my php code

    basically the line change points to the images directory.
    bloginfo(‘stylesheet_directory’);

    of course wrapped in php.

  24. Is there a way to overlay a transparent .png image over this? Basically what I am trying to accomplish is a nice border or a frame. The .png image I have is basically a frame. I want to give a layered effect/look to this where I have my ‘frame’ on top and this script runs beneath it.

    Hope that makes sense…

  25. Quantum, I would love to know more specifics of your solution. I didn’t really follow what you said, but am running into what I assume is a similar WordPress-related issue.

  26. Hi Mary I have used your great work in my new wordpress theme but its not working on the net. when I get the code locally its working but when I uploaded the net its not working ? can you check two sample for me ?

    internet version: http://www.wordpress24.net

    you can grab the source code from locally its will be works.

    I didn’t solve and find the problem.

    • @Bruce You have an error in some script:
      $("#hotnews").easyticker is not a function
      Try to remove that script and see if that is the problem. Hope it helps, ML

  27. A question remains: Can the sub-menus be aligned via the bottom edge vs. justified at the top? Here’s a site in progress (be gentle) that might help you understand the question:
    http://hmorthodontics.com/_index.php

    My sub-menus won’t all have the same number of links and I’d like them to display across the bottom edge vs the top. Is the answer in the CSS? The JS?

    Thanks in advance–LOVE your work
    Mike

  28. One word for that “Beautiful Background Image Navigation with jQuery” tutorial. WOW!

  29. @Fernando You have an error in your JavaScript: In the last line you have some repeated brackets. Hope it helps, ML

  30. @Dan We are really glad that you use this in your website. Of course we don’t mind and we appreciate the backlink although it is not necessary 🙂
    It really looks beautiful and I would encourage you to download the latest version and adding the part for preloading the images to your script. Then the next images will be shown immediately once the user hovers and it will look even nicer. Cheers, ML

  31. I dont get it, why is there a Sart hack at the header :

    *{
    margin:0;
    padding:0;
    }

    if I delete this, the margin goes funky, but why ? is there a way to get the correct margin with the CSS ?

  32. @Asuran, I guess that’s a bad habit 🙂 You can as well do this:
    body, div, h1, ul, li, [all elements to reset] { margin: 0; padding: 0; }
    Hope it helps,
    cheers, ML

  33. Hi Mary,
    I love your effect – great work thanks!. I have your effect working perfectly now and was ready to move on to the next stage in my freelance graphic designer website – http://www.nickjacksoncreative.com
    I don’t want any links to other html pages – I want to display my portfolio, cv and contact details etc. straight from this homepage using lightview as i did with a previous site design. Well i set it up as usual but the lightview feature won’t work ‘on top’ of your code eg. when you click on ‘view my portfolio’ for example it opens a new window with the sample image in it, as opposed to opening the lightview feature.
    So my question is how to combine your effect with the lightview effect to complete my site?
    Many thanks, Nick

    • Hello Nick! I get an error in the Prototype script saying that element.dispatchEvent is not a function. That’s probably some incompatibility with Prototype and jQuery. You should check out what issues there might be with Lightview and jQuery or with Prototype/Scriptaculous and jQuery. You should also take a look into http://api.jquery.com/jQuery.noConflict/, that might solve your problem. Hope it helps, Cheers, ML

  34. Thanks ML!
    The noconflict link you sent has fixed the problem by adding in the following”
    $.noConflict();
    jQuery(document).ready(function($) {
    Cheers, Nick

  35. Hi Mary
    I love this effect and want to use it, however I have just purchased dreamweaver and I’m not sure how to put it together. If you don’t feel you can help someone as basic as myself I understand, great effect regardless.

    Thanks

    Jamie

  36. Hi Mary

    Thanks for getting back, I know nothing really! I’ve tried downloading Jquery and I get a page of code but have no idea where to put it. That would be the first point, I think I know what to do after that but may come back to you if that’s ok.

    Thanks

    Jamie

  37. hi;
    I love your four column example and am trying to implement it.

    I would like to make four sections, and when in one section have the sub navigation activated.

    What is the correct method to make any one of the four columns the ‘active’ state (ie. sub-nav showing)

    thanks,
    Andrew C.

  38. hi;
    I would like to use your navigation toolkit plus a jquery slideshow within content pages. The two toolkit methods seem to be in conflict. Do you have any suggestions for implementing jquery slideshows within the body of webpages using your navigation tools?

    thanks!!!