O'Reilly

Create a Sprited Navigation Menu Using CSS and MooTools

By on  

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!

O'Reilly Velocity Conference
Save 20% with discount code AFF20

Recent Features

  • CSS @supports

    Feature detection via JavaScript is a client side best practice and for all the right reasons, but unfortunately that same functionality hasn't been available within CSS.  What we end up doing is repeating the same properties multiple times with each browser prefix.  Yuck.  Another thing we...

  • Serving Fonts from CDN

    For maximum performance, we all know we must put our assets on CDN (another domain).  Along with those assets are custom web fonts.  Unfortunately custom web fonts via CDN (or any cross-domain font request) don't work in Firefox or Internet Explorer (correctly so, by spec) though...

Incredible Demos

  • Input Incrementer and Decrementer with MooTools

    Chris Coyier's CSS-Tricks blog is everything mine isn't. Chris' blog is rock star popular, mine is not. Chris prefers jQuery, I prefer MooTools. Chris does posts with practical solutions, I do posts about stupid video-game like effects. If I...

  • CSS Fixed Position Background Image

    Backgrounds have become an integral part of creating a web 2.0-esque website since gradients have become all the rage. If you think gradient backgrounds are too cliche, maybe a fixed position background would work for you? It does provide a neat inherent effect by...

Discussion

  1. Anton

    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

  2. @Anton: Both of those worked for me easily.

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

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

  5. 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.

  6. 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.

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

  8. Abba Bryant

    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.

  9. 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.

  10. 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!

  11. 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.

  12. 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

  13. 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.

  14. Oooooh. Pretty! :D

  15. 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.

  16. Paul

    This is nifty, but what if you want to turn your menu into an include file so that it will dynamically call the JS and CSS?

    Currently you are indicating the home page and the about page are the current page in the php of the li….so, how would one go about setting this up?

    Thanks and super job!

  17. @Paul: If you want that you could use only the background Image and place the seperators and text dynamicly.

    That would require you to add a class to the li’s ( eg. li.MenuItem ) and style them all at once. The id would remain for JavaScript purposes.

    You could then use li.MenuItem:hover to change the text colors.

    The php would then use either a while loop or a foreach to render the li’s inside the ul.

    $q = mysql_query("SELECT * FROM menu_table ORDER BY sort");
    while( $row = mysql_fetch_array($q) ){
       if( $_GET['page'] == $row['id'] ){ $current = 'Current'; }
        echo '' . $row['text'] . '';
        $current = '';
    }
    

    * Although after reading through the javascript I don’t see the ID being used for anything but CSS styling. You would of course just create one master class for all the elements.

    From what it seems it would be

    ul
    
    li.MenuItem
    li.MenuItem:hover
    
    li.MenuItemCurrent
    

    This of course needs some modification to fit into your application but I hope this helps you as to how one would approach this.

  18. oops, the html rendres so I hand to make another go at this

    $q = mysql_query("SELECT * FROM menu_table ORDER BY sort");
    while( $row = mysql_fetch_array($q) ){
      if( $_GET['page'] == $row['id'] ){ $current = 'Current'; }
       echo '<a href="?page=' . $row['id'] . '" rel="nofollow">' . $row['text'] . '</a>';
       $current = '';
    }
    
  19. Paul

    @Arnar G: OH! Great response Arnar, thanks!

    Buuut, I guess I should have been more clear. I was thinking in terms of using it for specific images, such as a handful of thumbnails in a sprite format, for a portfolio. That way I wanted to use it as an include file for multiple pages.

    I’ve seen other solutions for CSS for hyperlinks and current page that identified the current page in the body tag as an id or at the top of the page with a PHP tag. Had hoped there might be an easy solution for this sample code too.

    Cheers!

  20. Jay

    Really cool.. :)

  21. shaik

    what if i have to call the .js without using php? can any 1 help me withthat…

  22. I have been trying to figure out how to make the link for the current pages. So far I have been unsuccessful in writing code that works.

    I have this:
    ul.rollover-ex li a:current { background-position: 0 -300px;}
    ul.rollover-ex li.button2 a:current { background-position: 0 -750px; }
    ul.rollover-ex li.button3 a:current { background-position: 0 -1200px; }
    ul.rollover-ex li.button4 a:current { background-position: 0 -1650px; }
    ul.rollover-ex li.button5 a:current { background-position: 0 -2100px; }
    ul.rollover-ex li.button6 a:current { background-position: 0 -2550px; }

    But it seems I may need to designate each button for the page it is associated with and I’m not sure how to write that in.

    Any suggestions?

  23. focusing not working, I even downloaded that .php file from your website and put it on my hosting but it’s not working yet, could please give me a hand where my mistake could be, thank you a lot

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!

Recently on David Walsh Blog

  • Prevent Chrome from Translating a Page

    A while back I shared my favorite Google Chrome extension:  Google Art Project.  I've enjoyed seeing beautiful art when I open a new tab -- it's brought genuine happiness to my day, however small that happiness may be.  About a week ago, however, the art presented had...

  • Create Any Type Of Website With These Multi-Purpose Themes

    We have selected what we believe are the very best multipurpose WordPress themes on the market today. Our list contains a number of best sellers, several newcomers that are proving to be highly popular, and a few themes that are ideal for creating the types of...

  • An Introduction to Static Site Generators

    Static site generators seem to have been becoming more and more popular recently, but they’re not one of those ephemeral novelty things that grow in popularity as quickly as they fall into oblivion shortly after. For over a decade, many different projects — 394 of...

  • Automated Tests for Visual Responsive Layouts

    Today it's all about testing. In 2015, many developers knows about TDD and I personally think that testing is one of the key for quality products. But what about testing in a Front-end environment? How do you guys write your tests for a responsive page or...

  • Getting Dicey With Flexbox

    What if you could build complex CSS layouts in minutes? Flexbox is a new CSS layout spec that makes it easy to construct dynamic layouts. With flexbox, vertical centering, same-height columns, reordering, and direction agnosticism are a piece of cake. There's a popular myth floating around that...