Create a Sprited Navigation Menu Using CSS and MooTools

Written by David Walsh on Thursday, October 29, 2009


CSS sprites are all the rage these days. And why shouldn’t be? They’re easy to implement, have great upside, and usually take little effort to create. Dave Shea wrote an epic CSS sprites navigation post titled CSS Sprites2 – It’s JavaScript Time. In his post he outlined a method for enhancing the CSS sprite menu with jQuery. I loved his post so much that I converted his jQuery CSS sprites menu to MooTools.

The Sprite Image

CSS Sprite Menu

This is a great example of an efficient, useful sprite image.

The HTML

<ul id="nav">
	<li id="home" <?php echo $current == '' || $current == 'home' ? 'class="current"' : ''; ?>><a href="?home">Home</a></li>
	<li id="about" <?php echo $current == 'about' ? 'class="current"' : ''; ?>><a href="?about">About</a></li>
	<li id="services" <?php echo $current == 'services' ? 'class="current"' : ''; ?>><a href="?services">Services</a></li>
	<li id="contact" <?php echo $current == 'contact' ? 'class="current"' : ''; ?>><a href="?contact">Contact</a></li>
</ul>

My code mostly mirrors the original ALA post’s code but I’ve chose to use IDs instead of CSS classes.

The CSS

#nav { width: 401px; height: 48px; background: url(sprites-blue-nav.gif) no-repeat; position: absolute; top: 100px; left: 100px; padding:0; }
#nav li { display: inline; }
#nav li a:link, #nav li a:visited { position: absolute; top: 0; height: 48px; text-indent: -9000px; overflow: hidden; z-index: 10; }

#home a:link, #home a:visited { left: 23px; width: 76px; }
#home a:hover, #home a:focus { background: url(sprites-blue-nav.gif) no-repeat -23px -49px; }
#home a:active { background: url(sprites-blue-nav.gif) no-repeat -23px -98px; }
#home.current a:link, #home.current a:visited { background: url(sprites-blue-nav.gif) no-repeat -23px -147px; cursor: default; }
.nav-home, .nav-home-click { position: absolute; top: 0; left: 23px; width: 76px; height: 48px; background: url(sprites-blue-nav.gif) no-repeat -23px -49px; }
.nav-home-click { background: url(sprites-blue-nav.gif) no-repeat -23px -98px; }

#about a:link, #about a:visited { left: 100px; width: 82px; }
#about a:hover, #about a:focus { background: url(sprites-blue-nav.gif) no-repeat -100px -49px; }
#about a:active { background: url(sprites-blue-nav.gif) no-repeat -100px -98px; }
#about.current a:link, #about.current a:visited { background: url(sprites-blue-nav.gif) no-repeat -100px -147px; cursor: default; }
.nav-about, .nav-about-click { position: absolute; top: 0; left: 100px; width: 82px; height: 48px; background: url(sprites-blue-nav.gif) no-repeat -100px -49px; }
.nav-about-click { background: url(sprites-blue-nav.gif) no-repeat -100px -98px; }

#services a:link, #services a:visited { left: 183px; width: 97px; }
#services a:hover, #services a:focus { background: url(sprites-blue-nav.gif) no-repeat -183px -49px; }
#services a:active { background: url(sprites-blue-nav.gif) no-repeat -183px -98px; }
#services.current a:link, #services.current a:visited { background: url(sprites-blue-nav.gif) no-repeat -183px -147px; cursor: default; }
.nav-services, .nav-services-click { position: absolute; top: 0; left: 183px; width: 97px; height: 48px; background: url(sprites-blue-nav.gif) no-repeat -183px -49px; }
.nav-services-click { background: url(sprites-blue-nav.gif) no-repeat -183px -98px; }

#contact a:link, #contact a:visited { left: 281px; width: 97px; }
#contact a:hover, #contact a:focus { background: url(sprites-blue-nav.gif) no-repeat -281px -49px; }
#contact a:active { background: url(sprites-blue-nav.gif) no-repeat -281px -98px; }
#contact.current a:link, #contact.current a:visited { background: url(sprites-blue-nav.gif) no-repeat -281px -147px; cursor: default; }
.nav-contact, .nav-contact-click { position: absolute; top: 0; left: 281px; width: 97px; height: 48px; background: url(sprites-blue-nav.gif) no-repeat -281px -49px; }
.nav-contact-click { background: url(sprites-blue-nav.gif) no-repeat -281px -98px; }

Unfortunately there’s plenty of CSS. That’s generally the one downside of using sprites but the payoff comes with less requests to the server. My CSS differs from the original ALA post in that I accommodate for my IDs.

The MooTools Javascript

(function($) {
	window.addEvent('domready',function() {
		$('nav').getElements('li').each(function(li) {
			//settings
			var link = li.getFirst('a');
			//fix background image
			if(!li.hasClass('current')) {
				link.setStyle('background-image','none');
			}
			//utility div
			var div = new Element('div',{
				'class': 'nav-' + li.get('id'),
				opacity: 0
			}).inject(li);
			//background imagery
			li.addEvents({
				mouseenter: function() {
					div.fade('in');
				},
				mouseleave: function() {
					div.fade('out');
				},
				mousedown: function() {
					div.addClass('nav-' + li.get('id') + '-click');
				},
				mouseup: function() {
					div.removeClass('nav-' + li.get('id') + '-click');
				}
			});
		});
	});
})(document.id);

My MooTools version is less code and slightly more efficient as I’m not creating and disposing of the same DIVs over and over as the user hovers over each menu item. The menu has original, selected, hovered, and mousedown states. Awesome!

Do you use sprites for any of your menus? Share a link to a few — I’d love to see what you’ve done!


Follow via RSS Epic Discussion

Commenter Avatar October 29 / #
Anton says:

Some bugs:

1. Demo page menu overlaps with the site footer.
2. When pressing mouse button on any of the menu items and then dragging the pointer out of the button and THEN releasing the button, the mousedown state is not reset to the original state, making the menu text move a little on hover.

Firefox 3.5.4

David Walsh October 29 / #

@Anton: Both of those worked for me easily.

Commenter Avatar October 29 / #

Oh my goodness! Could you format the CSS better please? :P

David Walsh October 29 / #

@Chris the Developer: Don’t pass out! If you understand one block for one navigation item, you understand them all!

Commenter Avatar October 29 / #

If you removed the text (very generic anyways), you could hugely reduce your css, and you gif, and make it modular, so you could easily take away or add any menu item.

I get sometimes you need the image text though.

Commenter Avatar October 29 / #
Arnar says:

I’m all for what Adam Meyer is saying.

The added comfort of being able to add navigation buttons from your CMS is great.

Besides the fact that search engines don’t like images in menus.

Commenter Avatar October 29 / #
Adriaan says:

@Adam Meyer: Yes, but then you have to use horisontal repeating backgrounds, which isn’t always applicable…

Commenter Avatar October 29 / #
Abba Bryant says:

Search engines don’t dislike images in menus. That is a bad generalization. For SEO this uses an image replacement technique. Look at the HTML and you can easily still see the plain link text in there.

Google etc. will index this fine.

One thing to note though, on FF 3.5.4 on OS X all of the inactive links except for the rightmost have an annoying pixel twitch going on. Likely just a bad negative position in the css.

Commenter Avatar October 29 / #
Arnar says:

Oh yeah, I didn’t read through the code but as you said it’s fine for search engines.

But it’s still a downside to have to modify the links using photoshop but not a CMS.

It’s all good though in cases were there is no need to edit the menu.

Commenter Avatar October 30 / #

I use this trick on my website…I absolutely love it and I receive countless compliments on the navigation on my site. I think the CSS isn’t that bloated…it’s trying to do a lot!

I’ve been using jquery to run the animation but I like this mootools answer…degrades just as nicely too. Thanks David!

Commenter Avatar November 01 / #

It’s strange to me to see all the comments about removing the text from the images for sake of modularity.

This would hardly be an article on *sprites* if there were no images. It’s a little like saying “You could just get rid of the CSS completely, and the javascript, and then the page would load faster.”

Any time you use sprites, you’re going to have a lot of CSS for all the background positions and typically suffer a hit to modularity. But some sites are high design, and that’s how you have to roll.

I usually have one file called `sprites.png` that’s an aggregation of almost all the pages graphics, and two named `repeat-x.png` and `repeat-y.png`, for obvious reasons.

Commenter Avatar November 04 / #

I believe I have everything set up properly, I have examined and re-examined the code, but I can’t figure out why the .current class isn’t registering. The file validates yet something is broken.

http://morganhirtle.ca/tmp/hirtles

Commenter Avatar November 04 / #
ugg uk says:

nice article and I agree with almost everything, but I think you create a new problem, where people are forced to make their point in the first three lines or 140 characters which isn’t necessarily a good thing.

Commenter Avatar November 06 / #

Oooooh. Pretty! :D

Commenter Avatar November 21 / #
Tim says:

Is it possible to use the standard mootools lib to get this, because of have other mootools gadgetss on teh same page and this and esp the snook nav didn’t play nicely with them because of different commands for teh same call. I am still just a noob.

Be Heard!

I want to hear what you have to say! Share your comments and questions below.

Name*:
Email*:
Website:  


© David Walsh 2007-2010. Contact David Walsh. Powered by the remarkable MooTools javascript framework.