Using jQuery for Background Image Animations

After reading Dave Shea's article on CSS Sprites using jQuery to produce animation effects, I felt like playing around with things to see what could be done but accomplish it with a simpler HTML structure (no need for adding superfluous tags) and simpler code, too.

Changing the position of the background image felt to be the best approach to creating the type of effect we're looking for (and I'm not the first to think so: see the examples at the end of this article).

jQuery is a great library for this type of task but out of the box, it can't animate background position properly because of the need to animate two values instead of just one (too bad not all browsers implemented the non-standard background-position-x and -y like Internet Explorer). You'll have to use the Background-Position plugin that is linked in the demo (the original plugin is no longer available on the jQuery site). Previous versions didn't support negative or decimal values properly.

The HTML

Nobody likes adding extra HTML to pull off all the fancy stuff and therefore, we're looking at a very simple unordered list:

<ul>
	<li><a href="#">Home</a></li>
	<li><a href="#">About</a></li>
	<li><a href="#">Contact</a></li>
</ul>

The Basic CSS

For this, I've just floated the navigation and set each of the links to display block with a little padding. Nothing fancy (which is the whole point).

ul {
	list-style:none;
	margin:0;
	padding:0;
}
li { float:left; width:100px; margin:0; padding:0; text-align:center; }
li a { display:block; padding:5px 10px; height:100%; color:#FFF; text-decoration:none; border-right:1px solid #FFF; }
li a { background:url(bg.jpg) repeat 0 0; }
li a:hover { background-position:50px 0; }

Notice that a hover state has been declared. Users who visit the page without JavaScript will, at least, still be able to view the final state. I've declared the background on the li a separately to make it stand out, but an initial background position needs to be set along with the background image you want to use for the effect.

The Image

You can pull off different types of effects by creating your image in a particular fashion.

Swipe

Figure 1: The Swipe

In Figure 1, the before and after states are on the left and right but a simple slant can create an interesting effect.

Swipe

Figure 2: The Fade

Figure 2 is a little more elaborate. The amount of visible space in the normal and hover states are at the very top and bottom of the image. The large gradient in the middle generates a fade-in/out effect when animated over time. The larger the gradient, the less it'll feel like it's moving in from the bottom and feel more like it's actually fading in and out.

The simplicity of the image allows the file size to be small, as well. More complex animations with more colour depth would require a larger file size. Always strike a balance between the effect and performance.

Check out the demo page to see how these two effects (and a couple others) look in action.

The Script

Finally, the script to put this altogether is really straightforward. The animation needs to run when the user moves their mouse over and out of the navigation.

$('#nav a')
	.css( {backgroundPosition: "0 0"} )
	.mouseover(function(){
		$(this).stop().animate(
			{backgroundPosition:"(0 -250px)"}, 
			{duration:500})
		})
	.mouseout(function(){
		$(this).stop().animate(
			{backgroundPosition:"(0 0)"}, 
			{duration:500})
		})

The elements are captured via the $ function and given a default CSS. This is necessary for both Internet Explorer and Firefox 2, which don't report the default background position. In IE, you can get around the issue using background-position-x and -y but it's easier just to set the initial values in the script.

Then the elements are animated using the mouseover and mouseout events.

The key thing to note is that any animation is stopped before attempting to animate again. This avoids animations queuing up from repeatedly moving the mouse in and out of the element.

jQuery also has a hover method which can be used instead of separately using the mouseover and mouseout methods. The reason I didn't was because I like the clarity that is provided by explicitly declaring them. The hover method takes two parameters: the function to run when over and the function to run when out.

The Demo

On the demo page, I've created four separate animations. Just hop to the source to see how it was all put together.

Other jQuery Examples

As mentioned, I'm certainly not the first to come up with this idea. Check out these other resources for more jQuery navigation animation exploration.

Update: Want to know how to handle active state? I got you covered.

Published September 22, 2008
Categorized as JavaScript
Short URL: http://snook.ca/s/913

Conversation

103 Comments · RSS feed
Chris Wallace said on September 22, 2008

This is very nice. Who needs Flash?

Anton said on September 22, 2008

This is very cool, I love how something so simple can create such an interesting effect.
I can already think of some places I'd like to use such an idea.

Chris Coyier said on September 22, 2008

Very slick. That top-down is my favorite. This is a poster child example for progressive enhancement (or graceful degradation, depending on how you look at it).

ET said on September 22, 2008

Great method! Definitely will be in my upcoming projects.

Pedro Rogério said on September 22, 2008

The example does not work in IE and Firefox, it seems to me that there was a problem with JavaScript.

jthomas said on September 22, 2008

It's a very cool effect but I tried to desactivate JavaScript and it wouldn't do anything. I mean, there's no alternative. Is it possible to have that cool JQuery effect, and, IF there's no JS, have the "simple" CSS effect ?

Tariq said on September 22, 2008

@Pedro works in Firefox here, check your browser

Tim Wright said on September 22, 2008

That's one of the cooler jQuery things I've seen lately.

Derek Allard said on September 22, 2008

Jonathan, brilliant. Its one of those :: smack head :: "why didn't I think of that" ideas. Well written as well, thanks for sharing.

Michael Hoskins said on September 22, 2008

I also like that you get a 'bonus' effect if you roll in and out of the button very quickly on the animated image examples, since the image simply tends to animate toward the final state, rather than jerking to the beginning of the mouseout state.

Jonathan Snook said on September 22, 2008

I updated the article to highlight the issue with setting the initial CSS, which should fix both IE7 and FF2 issues.

@jthomas: the demo page shows the noscript option which is what it could look like for users with no javascript.

Denis Kuznetsov said on September 22, 2008

it is very comfortable to use hover pseudo event (http://docs.jquery.com/Events/hover#overout) instead of mouseover and mouseout events in such cases -- less code

John David Anderson said on September 22, 2008

Adding the easing plugin would add an extra bit of icing to the cake here (though I think there are two core easing options as well).

Neal G said on September 22, 2008

Thanks Snook! I was a bit aggravated with Dave's demo and all the extra classes he had in the code. I also did not like the fact that he used a hidden div to overlay over the menu items. Snook 1, Shea 0.

James said on September 22, 2008

Examples C & D can be accomplished without images by using the official colour animations plugin and the animate() function.

Jonathan Snook said on September 22, 2008

@James: indeed it could. However, interestingly, the use of image + background-position plugin may end up smaller than using the color-animation plugin. But always nice to have options. Thanks for pointing it out.

Matthew said on September 22, 2008

I too like this better than Dave's version which I had started incorporating into a new design buit will now switch, however this version doesn't have the "active" state setup. I guess that would be easy to do... but I'm lazy and would rather copy/paste.

Jason Beaird said on September 22, 2008

Great writeup Jonathan! As I was reading Dave's ALA article I kept thinking the same thing...why wouldn't you do it with an animated background? I'll definitely be using this type of effect on future projects.

While it isn't nav/sprites related, I recently used JQuery and the background-position plugin on a client project. Thought I might as well post up a quick demo here in case anyone is trying to do something similar.
Background Image Animation Demo

icaaq said on September 22, 2008

Really, really nice.

Adriaan Nel said on September 22, 2008

I like it, think I will definitely use this. Although I gotta add that I prefer mootools to jquery.

travis beck said on September 22, 2008

nice,
I really like the second example with the wave.
I also love how you can endlessly chain methods together in jQuery.

Dave S. said on September 22, 2008

Wow, this is simpler and more elegant than my article for sure. I never even thougt of background-image animation. Nicely done.

Cameron Westland said on September 22, 2008

I've been using this effect for a while, playing with animation the opacity property for really interesting effects!

Dever said on September 22, 2008

Thanks for the stop().animate(...) bit, I was missing the stop() and my animations queued up. To prevent that I tried animate( queue:false, ... ) but that didn't work as expected.

Ricardo said on September 22, 2008

I like it so much.
And I will use in any future project for sure.

Tom said on September 23, 2008

Excellent. Loved the ALA article but thought it was a little heavy but this is excellent.

Now to find a site I can use it on!

Gilbert said on September 23, 2008

Wow. I can't believe how much that looks like flash. Great effect.

Andreas Stephan said on September 23, 2008

This rocks! Thanks

Jowra said on September 23, 2008

Looks nice, but in the end its just the continuation of manipulations of the position of background-images with CSS (like with image-rollovers). Only the creativity sets the limits of animating CSS properties with JS.

Just an idea: Maybe its more helpful to a greater audience, if such things are described in a more general way and more as an impulse to get started. There are still many other JS frameworks out there and in use.

Anup said on September 23, 2008

This is great. I have seen a few other ones in recent weeks, and used a hybrid myself, but I like this approach. One accessibility enhancement suggestion, however: use the focus and blur events to run the same code as mouseover and mouseout events, for keyboard accessibility. I just tried it locally and it worked quite nicely with keyboard access too.

Flexer said on September 23, 2008

Another cool trick is to use

$('#nav a:not(:animated)').mouseover( this.animate [...] )

instead of the .stop().animate()
It will actually leave the original animation to finish instead of stopping and restarting it.

Lauren said on September 23, 2008

this literally could not have come at a better time! I've been fooling around with a menu using the sprite technique and the client really wanted flash. I just didn't see the need for the menu to be flash. This plug-in will work perfectly!

Thanks Snook - You rule!

^__^

S.K. said on September 23, 2008

Awesome plugin. But how do I create a variable that retrieves only the x background-position of the element and then use that variable in place of the x in backgroundPosition?

Here is my n00b attempt

$('#nav a')
	var positionX = $(this).css("background-position-x");
	.css( {backgroundPosition: positionX + "0"} )
	.mouseover(function(){
		$('#nav a').stop().animate(
			{backgroundPosition:"(" + positionX + "-550px)"},
			{duration:500})
		})
	.mouseout(function(){
		$('#nav a').stop().animate(
			{backgroundPosition:"(" + positionX + "0)"},
			{duration:500})
		})
ralexismf said on September 23, 2008

Beautiful animated, great, not need flash!

Veera said on September 23, 2008

Really impressive.

Mark Story said on September 23, 2008

Very Nice Jonathan! I like that there is no additional markup required. Keeps everything clean and simple. Kudos on the clever use of background-position.

Jojo Siao said on September 23, 2008

thanks for the tutorial!

Jojo Siao.

Fatih CERITLI said on September 24, 2008

Thanks.

Realy gj.

Erwin Heiser said on September 24, 2008

Seriously sweet this! I really like using the gradient image for a subtle color fade.

Dave said on September 24, 2008

When I turned off Javascript, the hover states do not turn on. This is a specificity issue with the CSS. I see you addressed this sorta, but not completely. Adding this the CSS seems to fix the issue:

#a a:hover,#a li a:focus,#a li a:active,
#b a:hover,#b li a:focus,#b li a:active {
background-position:-150px 0px;
}
#c a:hover,#c li a:focus,#c li a:active,
#d a:hover,#d li a:focus, #d li a:active {
background-position:0px -250px;
}

Jonathan Snook said on September 24, 2008

Dave: that's why I presented a no script option at the top of the page. The other examples demonstrated the script. It was an exercise for the user to put it all together. :) (and thanks for doing so...)

Tom said on September 25, 2008

I will use it. thanks

Zack Katz @ Katz Web Design said on September 25, 2008

Wow, thanks a lot! This will be a great thing for clients who want to have Flash navigation on their websites. You could also do color fade effects in combination for additional visual trickery.

James said on September 26, 2008

Nicely done Jonathan!

Fredrik W said on September 26, 2008

I wrote a similar tutorial a while ago that demonstrated how can you fade between two images to create a similar effect. I took a similar approach to yours. For those interested, it can be found at http://swedishfika.com/2008/03/04/creating-a-fading-header/

FruJo said on September 29, 2008

Great job.
Simple and at the same time pretty usage of jQuery. I wrote a russian translation of this article at http://habrahabr.ru/blogs/webdev/40801/ and it achieved a lot of positive feedback. Thanks a lot.

Andrew Pawson said on September 29, 2008

Jonathan: Nice post. FYI - perhaps it's the firewall I'm behind, but I get 403 (Unauthorized) errors (via Firebug and Safari error console) for both the core jQuery library and your plug-in .js files.

chris said on September 30, 2008

thanks, these are some great example and will be very useful :)

Jesse Vlasveld said on October 03, 2008

Liking this alot, it could produce some awesome effects. Even saw it in use a couple times already, it sure is some good eye candy.

Joel Sutherland said on October 06, 2008

I turned this technique (specifically Shea's CSS Sprites2) into a jQuery plugin:

http://www.newmediacampaigns.com/page/css-sprites2-refactored-building-an-unobtrusive-jquery-plugin

Hopefully it is helpful!

rama said on October 06, 2008

nice article. exactly what i needed. thanks jonathan.

Davide Di Cillo said on October 06, 2008

very nice script!

Sulcalibur said on October 08, 2008

Superb post my friend. We need more posts like this (hint hint) ;)

Robin said on October 08, 2008

"Who needs Flash?"

Abso-frickin-lutely! DOM animation for the win! Beautiful stuff, I may be using something similar of my own in the near future. Cheers for the infos!

...and is that CakePHP I see powering this site? Kudos to you if it is :)

Robin

chris simpson said on October 09, 2008

Why didn't i think of this? So simple, yet so effective, i love the examples too. Thanks !

Josh Langner said on October 09, 2008

Excellent! Now I've got a bigger challenge for you: How about changing the opacity of the background image, or at least simulating such an effect, without adding to the complexity of the XHTML markup? I've already spent some time working with this but alas without success.

Luc said on October 10, 2008

Excellent - I've stolen it as part of my default toolset from hereon in...

Thomas said on October 17, 2008

Very great and original plugin. jQuery meets flash!

insic said on October 22, 2008

im really impress of this technique. thanks fro sharing.

ShopDev said on October 22, 2008

Probably the most elegant method I've seen. Really very simple code.

Steve said on October 25, 2008

Very nice! Clean code, simple jquery, thanks!

Mat said on October 27, 2008

Is there a way to leave the background in it hover position is a certain class is attached the li?

Salwa said on October 28, 2008

Ok this awesome! Thank you!

Morrocan baker said on October 31, 2008

i liked the effect especially the first one, did you have any prototypeJS plugin who do the same <= [lazy develloper] lol

Sasha Sklar said on November 11, 2008

Something to be aware of is jQuery's hover function actually uses the mouseenter/mouseleave events (either the proprietary IE version or an emulation of them in other browsers) whose behavior is somewhat distinct from mouseover/mouseout.

Jesse said on November 13, 2008

Well I copied everything verbatim from the test page (images, js, the whole bit) to play with in on our test server, but it doesn't work. At all. Very strange. The "wave" animation gets stuck in the middle, and the rest get stuck too. Oh well, off to find another slick jQuery navigation trick. :(

Anthony said on November 15, 2008

Inspired by what I saw on this post I'd hoped it could be replicated in Script.Aculo.Us so I could apply it to my existing apps.

Sadly Scriptaculous' *Effect.Morph* doesn't anticipate dual values so can only move backgrounds horizontally (it's been logged as a bug back in July).

Delighted by your examples, I've ported the methods over into an extension for Scriptaculous so now it works. Hopefully they'll see your examples and support it natively soon.

Cheers for sharing all your hard work.

rizki a. tyas said on November 17, 2008

saya ingin buka blog foto

derrick said on November 23, 2008

this script absolutely does not work. i would not waste your time. the author has obviously used additional scripting to get it to work. This tutorial was written for search engine ranking and nothing else. Free stuff gets hits. :(

Jonathan Snook said on November 23, 2008

Derrick: you can view source to see exactly how it's been done. I can't make it do something special that nobody can see. If you're having problems, ask questions.

derrick said on November 23, 2008

**TUTORIAL ADDED TO JUNK TUT DATABASE**
This tutorial has been added to the Junk Tut Database as part of the Junk Tutorial Project to be launched soon to help unsuspecting web users avoid time-wasting tutorials such as this.

Thanks

Drew said on November 23, 2008

Snook, thanks so much for taking the time to write this. Works great.

@derrick, just because you aren't competent enough to get this to work doesn't mean its broken. It works just fine for me. Please take your arrogant and threatening comments somewhere else. I'm sure you will be added to the akismet spam DB if snook uses it, at least I would.

derrick said on November 23, 2008

@drew,

I guess you're not too sharp. You have to copy and paste script off others to get the job done. I'm not the one posting junk code.

derrick said on November 23, 2008

@drew,

you also need to read the akismet site again. my comment hardly qualifies for spam. crying wolf to akismet is a good way to get your own site banned.

Jonathan Snook said on November 23, 2008

Worked out the issue with Derrick via email. There was a missed file in the setup.

derrick said on November 24, 2008

The problem was actually partially solved.

Jonathan Snook said on November 24, 2008

@derrick: no, it was completely solved. I just double-checked the test page you had sent me via email and the one animation that doesn't "work" is because you're animating along the X axis when the image only changes along the Y axis.

Dave said on November 25, 2008

We've put this concept into use in our game, http://www.vertigo-project.com/projects/redline-game

Essentially the background 3 divs (road, grass and cloud) move at varying speeds by just adjusting the background position...

Tim said on November 29, 2008

Snook lovely work. How hard would it be to apply this to a whole page background I wonder !?

Steve said on November 29, 2008

@Tim: seems like it would apply in the same way. just change "#nav a" to "body" or whatever and give it a try.

Patrick said on December 02, 2008

Very nice. I've never tried jQuery before, but this inspires me to give it a look. Thank you.

elvisparsley said on December 08, 2008

I wonder if this works with different lengths of menu.
Anyone, any idea??

Jonathan Snook said on December 09, 2008

It should work with whatever length of menu you have, assuming the image you use is designed for the length of the menu item.

esthezia said on December 10, 2008

Hello. Great method, i love it. I just want to ask if it would be posibile for a FADE transition. fade background images one to another. Thank you!

Renee said on December 20, 2008

I'm getting a flickering effect on IE6, anyone have a fix for this?
When will people stop using that browser so I can ignore it?!

Flepi said on January 14, 2009

Hello Jonathan,

You are probably very busy but I want to add the effect : When you click on a header link, the background stay in the second color (active tab). In wordpress there is a "current_page_item" we must add in CSS to do so but I don't success...

If you could explain how to do so with jquery or css...

Thanks a lot !

Frank said on January 16, 2009

Great post! I just found this awesome jQuery Cheat Sheet for the iPhone. I've only had it for a day and it's been really helpful.

http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=302090867&mt=8

Looks like there's also a CSS Cheat Sheet from the same guys.

http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=301093674&mt=8

Sean said on January 26, 2009

Great post! This opens up doing some new types of mouseovers I hadn't thought about before.

Thanks for posting!

Goo said on February 01, 2009

Very nice. actually the background animation can have some variations. Simply adding a png format image serving as a text mask can create a very nice effect. I have made a page according to your inspiration. see my site for demonstration

pab said on February 02, 2009

This is great, love the simplicity of it and it works like a charm, well done and thanks for sharing.

jason said on February 02, 2009

I'm trying to add a class selected to it.

anyone have an idea on how to go about this?

Ben Cachel said on February 11, 2009

@derrick it works fine! Same as per this demo page, try to install the script again.

Thank you Jonathan. I love this way for buttons, much better than old animated .gif images :) and quicker too. Will play around possibly adding some CSS in addition. cheers

Aaqib said on February 20, 2009

its really cool,,, thnx man

Burt said on February 15, 2011

Can't get this to work with jquery-1.4.4.min.js but is fine with 1.2.6.min

Peter said on February 23, 2011

Wonderful Jonathan - thank you.

@ world, who is this Derrick guy?

Mike said on February 25, 2011

Darn. I've been trying to get it to work with jQuery v1.4.4 as well. Anyone have any luck? What should one change to make it work?

(thanks for any help!)

Norja said on February 25, 2011

This is great, but what is the dimension of the background image for the remainder versions? I like the one "top down" one. Thanks!

June said on March 21, 2011

hello. i love this idea and currently trying to implement on my site but having some issues with handling active state. is there a way to disable mouseout animation once clicked? Thanks in advance!

Jonathan Snook said on March 21, 2011

@June: Check out the follow-up article on active state. This might explain what you need.

June said on March 22, 2011

Jonathan, thank you for the quick reply. Follow-up article makes sense but I was hoping you would explain how to do it using 'onclick', 'addclass' type of route or add conditions at the beginning. The reason for this (for me) would be to 'include' menu as part of header through php. Well maybe when you have time you could expand this tut more.

Jonathan Snook said on March 22, 2011

@June, in that case, you'd add an click handler that toggles an active class. Then include a conditional that checks for the existence of the active class before continuing the animation.

Charles P. said on April 15, 2011

@Jonathan,

This is a wonderful snippet of code. It took me a little bit of a moment to figure it out (due to my lack of solid CSS and ASP.NET masterpage knowledge), but it was fairly easy to follow. Any ideas on how to get rounded edges for this concept, without any IE/other browser hacks?

Mary said on May 18, 2011

Jonathan,
thank you for funtastic plugin! I can see that your example works just fine in IE7 but mine shows only hover state. Do you have any idea why? Thanks again.

Sorry, comments are closed for this post. If you have any further questions or comments, feel free to send them to me directly.