In particular, Nathan Wrigley of pictureandword.com, needed a method one image into another mouse rolls over, and the slowly fade back once the mouse has left.
Image rollovers were the staple JavaScript nugget of the 90s, and for a lot of JavaScript developers I know, one of the starting places their passion for JavaScript. Today, rollovers are a no-brainer - either in CSS or with the simplest of JavaScript:
$(function () {
$('img.swap').hover(function () {
this.src = 'images/sad.jpg';
}, function () {
this.src = 'images/happy.jpg';
});
});
Today’s challenge is the rollover transition!
Watch the complete screencast (alternative flash version)
(QuickTime version is approx. 20Mb, flash version is streaming)

How to Approach the Problem
There are a few different ways which problem can be solved (and I’d love to hear alternative methods via the comments).
Here are the ways I’m going to go through:
- Two Image
- Single Image
- Pure CSS
The key to all of these techniques is how the rendered markup (i.e. what the browser finally sees) is arranged: all of which are very similar.
Essentially, the end image for the transition must sit absolutely in the same position as the starting image.
It’s also worth keeping in mind that the images we fade between should be the same size (height & width-wise).
Note: all three of these techniques have a caveat: styling the start or end image may cause the effect to break. I would recommend wrapping the image in a div or span and styling that element, as it will require less changes to the JavaScript. Either way: it is always best to test in the target browsers.
Two Image Technique
I should start by crediting Karl Swedberg who runs Learning jQuery. He solved Nathan’s transition problem using the following technique.
Karl’s method starts with the two images in the markup: both the start and end images. They are contained in a div and the end image is contained in a further div with absolute positioning.
It is important to note that this technique works best for absolutely position images. Changing the div.fade to position: relative means the div element remains as a block element, and div will stretch the with of it’s container element (defaulting to 100%).
View the working example and the source
HTML
<div class="fade">
<a href="/info.html"><img src="start.jpg" /></a>
<div>
<a href="/info.html"><img src="end.jpg" /></a>
</div>
</div>
CSS
Obviously if I had more than one fading image, I would use an ID or alternative class to position the top and left CSS properties.
.fade {
position: absolute;
top: 100px
left: 100px
}
.fade div {
position: absolute;
top: 0;
left: 0;
display: none;
}
jQuery
// when the DOM is ready:
$(document).ready(function () {
// find the div.fade elements and hook the hover event
$('div.fade').hover(function() {
// on hovering over, find the element we want to fade *up*
var fade = $('> div', this);
// if the element is currently being animated (to a fadeOut)...
if (fade.is(':animated')) {
// ...take it's current opacity back up to 1
fade.stop().fadeTo(250, 1);
} else {
// fade in quickly
fade.fadeIn(250);
}
}, function () {
// on hovering out, fade the element out
var fade = $('> div', this);
if (fade.is(':animated')) {
fade.stop().fadeTo(3000, 0);
} else {
// fade away slowly
fade.fadeOut(3000);
}
});
});
Single Image Technique
This takes the two image technique further. I like the idea that we should let the JavaScript add the sugar to the markup - in that we should really only want an image tag, and using some method to know what image we want to fade to.
This technique allows us to insert the image in the markup as we would if there were no transition effect, and the image can be inline, rather being positioned absolutely.
We are going to use the background-image CSS property to specify the target image to fade to.
View the working example and the source
HTML
<img class="fade"
src="images/who.jpg"
style="background: url(images/who_ro.jpg);" />
CSS
Other than the inline background image - none is required. You can also apply the background-image using classes if you like.
If we wanted to absolutely position the image, or float: right for instance, the best way to do this (if we want to keep the transition), would be to wrap it in a div and style that element.
jQuery
Using jQuery, we execute the following tasks:
- Wrap the image in a
span - Insert a new image, whose source is the
background-imageof our start image - Position the new image so that sits directly behind the starting image
- Bind hover event to start the effect
// create our transition as a plugin
$.fn.crossfade = function () {
return this.each(function () {
// cache the copy of jQuery(this) - the start image
var $$ = $(this);
// get the target from the backgroundImage + regexp
var target = $$.css('backgroundImage').replace(/^url|[()]/g, ''));
// nice long chain: wrap img element in span
$$.wrap('<span style="position: relative;"></span>')
// change selector to parent - i.e. newly created span
.parent()
// prepend a new image inside the span
.prepend('<img>')
// change the selector to the newly created image
.find(':first-child')
// set the image to the target
.attr('src', target);
// position the original image
$$.css({
'position' : 'absolute',
'left' : 0,
// this.offsetTop aligns the image correctly inside the span
'top' : this.offsetTop
});
// note: the above CSS change requires different handling for Opera and Safari,
// see the full plugin for this.
// similar effect as single image technique, except using .animate
// which will handle the fading up from the right opacity for us
$$.hover(function () {
$$.stop().animate({
opacity: 0
}, 250);
}, function () {
$$.stop().animate({
opacity: 1
}, 3000);
});
});
};
// Not only when the DOM is ready, but when the images have finished loading,
// important, but subtle difference to $(document).ready();
$(window).bind('load', function () {
// run the cross fade plugin against selector
$('img.fade').crossfade();
});
Pure CSS Technique
If I’m honest, this final technique is a bit cheeky - but still valid. It uses CSS animations only (currently) available in Safari 3 (and WebKit).
However, this is a great example of how to the leverage the CSS using an iPhone (over using JavaScript).
The HTML is the same rendered HTML from the single image technique - but it requires zero JavaScript.
View the working example and the source (to see the effect, view using Safari 3).
HTML
<span style="position: relative;">
<img src="images/who_ro.jpg" />
<img
style="position: absolute; left: 0px;"
src="images/who.jpg" class="fade" />
</span>
CSS
Although this is only supported in Safari 3, the roll over still works in Firefox (and could work in IE7 - though not IE6 because :hover only works on anchors) - because it’s changing the image’s opacity on :hover.
img.fade {
opacity: 1;
-webkit-transition: opacity 1s linear;
}
img.fade:hover {
opacity: 0;
}
Taking it Further
I’ve taken the single image technique further in to a complete plugin. It’s designed to allows us to pass options to control the type of bind, delays, callbacks and tests before running the animation.
You can see the plugin in action in this simple memory game I put together quickly.
It pulls the latest photos from flickr, shuffles them, and then sets your memory skills to work.
It’s obviously just a quick prototype - and I’m not sure what happens when you go beyond level 5! Enjoy.
You should follow me on Twitter here I tweet about jQuery amongst usual the tweet-splurges!
Related posts
Demo
If you find this demo doesn't work as expected, it's possibly due to the demo running from within an iframe. Try running the demo in it's own window.
Source Code
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>Fade Method 2 (single image)</title>
<style type="text/css" media="screen">
<!--
BODY { margin: 10px; padding: 0; font: 1em "Trebuchet MS", verdana, arial, sans-serif; font-size: 100%; }
H1 { margin-bottom: 2px; }
-->
</style>
<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript">
<!--
// wrap as a jQuery plugin and pass jQuery in to our anoymous function
(function ($) {
$.fn.cross = function (options) {
return this.each(function (i) {
// cache the copy of jQuery(this) - the start image
var $$ = $(this);
// get the target from the backgroundImage + regexp
var target = $$.css('backgroundImage').replace(/^url|[\(\)'"]/g, '');
// nice long chain: wrap img element in span
$$.wrap('<span style="position: relative;"></span>')
// change selector to parent - i.e. newly created span
.parent()
// prepend a new image inside the span
.prepend('<img>')
// change the selector to the newly created image
.find(':first-child')
// set the image to the target
.attr('src', target);
// the CSS styling of the start image needs to be handled
// differently for different browsers
if ($.browser.msie || $.browser.mozilla) {
$$.css({
'position' : 'absolute',
'left' : 0,
'background' : '',
'top' : this.offsetTop
});
} else if ($.browser.opera && $.browser.version < 9.5) {
// Browser sniffing is bad - however opera < 9.5 has a render bug
// so this is required to get around it we can't apply the 'top' : 0
// separately because Mozilla strips the style set originally somehow...
$$.css({
'position' : 'absolute',
'left' : 0,
'background' : '',
'top' : "0"
});
} else { // Safari
$$.css({
'position' : 'absolute',
'left' : 0,
'background' : ''
});
}
// similar effect as single image technique, except using .animate
// which will handle the fading up from the right opacity for us
$$.hover(function () {
$$.stop().animate({
opacity: 0
}, 250);
}, function () {
$$.stop().animate({
opacity: 1
}, 250);
});
});
};
})(jQuery);
// note that this uses the .bind('load') on the window object, rather than $(document).ready()
// because .ready() fires before the images have loaded, but we need to fire *after* because
// our code relies on the dimensions of the images already in place.
$(window).bind('load', function () {
$('img.fade').cross();
});
//-->
</script>
</head>
<body>
<h1>Fade Method 2 - Single Image Technique</h1>
<p>This technique uses jQuery to modify the markup and to animate to fade transition.</p>
<p><a href="http://jqueryfordesigners.com/image-cross-fade-transition/">Read the article, and see the screencast this demonstration relates to</a></p>
<div>
<img class="fade" src="images/who.jpg" style="background: url(images/who_ro.jpg);" alt="Who we are" />
<img class="fade" src="images/who.jpg" style="background: url(images/who_ro.jpg);" alt="Who we are" />
<img class="fade" src="images/who.jpg" style="background: url(images/who_ro.jpg);" alt="Who we are" />
</div>
<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "UA-1656750-8";
urchinTracker();
</script>
</body>
</html>
New
Play QuickTime version
Play Flash version
Jay On 1st June 2008 at 19:06
Uhhh my post was deleted!!!
i know it may sound stupid but where do you get the jquery.js script i tried copying the one above but no success
Remy On 1st June 2008 at 20:06
@Jay - have you tried jquery.com? If not head over there. It’s a pretty good starting place. Good luck!
Peter On 2nd June 2008 at 22:06
(Hmmm, didn’t realize that the comment system parses HTML. Feel free to delete that last comment.)
Thanks so much for your code. I’m currently implementing the single image method in the redesign of my company’s website, and it’s a fair amount more elegant than the other methods that I’ve tried.
One question, though: While the rollover effect worked fine in Firefox, Safari, etc, IE6 and 7 both positioned the start image directly below the DIV in which I placed the <img class="fade" …>, instead of on top of it, so you could see both the start and end image at the same time (one above the other in the same "layer," if you will). The img element is also wrapped in an <a> tag so the image links to another page.
I was able to fix this by messing with the JavaScript a bit… I moved $.browser.msie to the next conditional statement so that IE gets the same CSS treatment that Opera does, particularly the "top:0" rule. So the problem’s out of the picture now, but I’m still curious as to why it happened.
Any idea why IE would shove the start image down 220px (the height of my start and end images) instead of positioning it in the same place as the end image? If you’d like to see the site and code in place, please email me and I’ll send you the development site’s IP address (I’d rather not post it here, because I don’t want search engines indexing it yet).
Thanks again!
Mr. Wolf On 4th June 2008 at 21:06
Trying to implement version two with one ( for now ) image to test. “../../img/cd_dvd_bd/wedodiscs.jpg” However, as you can see I am not using your code correctly.
Would you mind pointing out what might be the issue.
Thanks
Remy On 4th June 2008 at 21:06
@Mr Wolf - I’ve just checked your URL ( http://www.glmt.com/concepts/concept02/main/cddvdbd/index.php ) and it appears to work fine in Safari and FF2 (I’m on a Mac). What exactly is the issue?
Pernicio On 5th June 2008 at 11:06
Hi there, I’m interested in implementing this to my website, but it is quite hard, as I know next to nothing about coding :D so far I have managed with creating outlooks with photoshop and positioning etc. with wysiwyg and tweaking little by little with dreamweaver, but javascript is the hardest of them all so far. I have no idea how to implement/reverse engineer this to work with my site. I would be glad if someone could assist me in making the method 1 work in my site with my link images. I have tried both, method 1 and 2 but couldn’t get neither working correctly, as you can see in my web page, the first link is a failed test. It always shows that box around the image and with method two, it harasses the transparency of the sites buttons. If there is a way to make it that way, that the background image of the method two fades away as the second image fades in, I would be glad to implement that. But primarily I would want either one of those work in my site without glitches.
Mr. Wolf On 5th June 2008 at 12:06
Thank’s for your quick response!
I have ../../js/jquery.js, ../../js/mootools_1.11.js, ../../js/sliding-tabs.js, and ../../js/gradualfader.js + your JS. If I comment out “<!– –>” all other JS but yours, the “WEDODISCS.” button works nicely. Thanks, once again.
However, when all JS is allowed to load, a conflict happens with your JS. Could you manipulate #2 JS to work with the above JS libraries?
When you tested the above URL, did you notice that I had removed all other JS for testing? I’m hoping that what you viewed only had your JS.
This is out of the scope of our conversation, but I am also looking for a solution that would allow a user to click a button on the first index page (opening page) and load …/main/cd_dvd_bd/index.php in a new window with slider function moved to “Triple Wallet” for instance. - Could I take this off line with your e-mail address?
Pernicio On 18th June 2008 at 22:06
Ok, I got it working, but is there any way of fixing the jamming that occurs if mouse is passed over them too rapidly? For example, could image catching help and how to implement the fix?
ONUR On 30th June 2008 at 14:06
I love this effect/tutorial! I’m new at coding but I’ve successfully implemented method 1 with one image on my page. What I need help with is how to take method 1 and apply it to multiple images on a page. I’m sure the solution is easy, but like I said - I’m new.
ONUR On 30th June 2008 at 16:06
I figured out the multiple image problem I was having above, but now I’m having the same issue as Pernicio - the images are jamming when the mouse passes over them too rapidly. Any suggestions?
Mike On 30th June 2008 at 20:06
Great tutorial. I’m trying to implement it in the icons at a site in progress. For some reason, ie6 and ie7 do not animate the fadeIn and fadeOut correctly. They just seem to toggle the opacity. I’m using an a tag for the icons with css image replacement. Any suggestions would be greatly appreciated. http://208.79.237.221/
Mike On 1st July 2008 at 18:07
Got it working with a simplified version. Thanks anyway.
Markive On 10th July 2008 at 15:07
The pure CSS technique doesn’t seem to work on my Iphone as mentioned… Plus there is no way to ‘hover’ over a button. It’s touch or no touch…
Remy On 10th July 2008 at 15:07
@Markive - yep, you’re right, this particular effect isn’t going to trigger on the iPhone because there’s no hover state on the iPhone. The point was more to show off the functionality with Safari’s built in CSS. There’s a few other states missing from the iPhone due to it’s method of UI - not that that’s bad - just different ;-)
Radu On 15th July 2008 at 20:07
If you put the image in a link and in a div in doesn’t work correctly in Opera 9.51. What can the problem be?
Lashan On 18th July 2008 at 22:07
Okay, well, I tried this out on my site and it worked perfectly in Firefox 3 & Safari, but not so much in IE 7 or Opera 9.5.
Here it is - http://anasatzqui.net/site/index.html
IE is just plain messed up, and in Opera if you load the page, then resize it and try roll over, it’s messed up.
Thanks for any help.
Greg Johnson On 21st July 2008 at 09:07
I’ve adapted the first technique for a project I’m working on and spoke on the issue here: http://greg-j.com/2008/07/21/hover-fading-transition-with-jquery/
I’m having a hard time solving an issue you spoke about regarding animating the class that triggers the hover itself though, so if you have any input I’d love to hear it.
(demo:http://greg-j.com/static-content/hover-fade.html )
djai On 1st August 2008 at 04:08
Thank you for making this site available. The information has been very helpful.
Since finding out about this site, I’ve been experimenting with jQuery Image Cross Fade Transition on the following site http://adsitap.com/adsitfadetest.html
The problem I’m running into is once I link the images I can’t seem to get rid of borders on both the active and hover images. I tried specifying within the .css file properties for links to be “text-decoration: none; border: 0px; margin: 0px;”
I also added border=”0″ as well as style=”border: 0px;” within the tag. The first, border=”0″ seems to work for the active state, but how do I get rid of borders for the hover state? Please advise. Thanks in advance for you time.
chris On 1st August 2008 at 14:08
This is great, I got it working..however how do I get it working so it just fades in and out of images without the hover state.
Thanks!
djai On 2nd August 2008 at 04:08
Just thought I try re-submitting this comment/issue again:
Since finding out about this site, I’ve been experimenting with jQuery Image Cross Fade Transition on the following site http://adsitap.com/adsitfadetest.html
The problem I’m running into is once I link the images I can’t seem to get rid of borders on the hover images. I tried specifying within the .css file properties for links to be “text-decoration: none; border: 0px; margin: 0px;”
I also added border=”0″ as well as style=”border: 0px;” within the tag. The first, border=”0″ seems to get rid of the active state, but how do I get rid of borders for the hover state? Please advise. Thanks in advance for your time.
Bruno On 9th September 2008 at 16:09
Hi, Anyone find out a fix for IE6? On IE7 it isn’t perfect too.. I’m working with png’s with transparency, it’s ok on firefox but a mess on IE6. I’ve tried a couple of things said on previous comment, but none worked.. any idea? Thanks in advance!
xylpho On 28th September 2008 at 09:09
Hello there,
I’m trying to implement the second method in combination with JQuery Cycle Plugin (http://malsup.com/jquery/cycle/pager2.html) but the crossfade effect don’t work as expected. You can see it here : http://www.nicolas-guilhou.com/FotoTrafik , choose the “retouche” tab. I’m a noob in JS and don’t understand why it don’t work, any help would be appreciated. Thanks in advance ;)
Danny On 15th October 2008 at 21:10
Hi, I am trying to get this script working on a site of mine and have a few problems. The roll overs work fine when displaying only 3 images across. As soon as I add the fourth things get a bit muddled.
Any chance some one could help me with this? Much appreciated.
Here is the link to the page I’m using the script on:
http://www.adayin.co.uk/pp/clients.htm
Danny On 15th October 2008 at 21:10
Sorry, forgot to mention it works fine in firefox and safari. IE7 is the problem.
Danny On 15th October 2008 at 21:10
Sorry, i forgot to mention it doesn’t work as wanted in IE7.