I live in the sometimes sunny Brighton (it's in the south of the UK, for those across the pond). The south coast is definitely my favourite place to be, but I spent some time on the outskirts of London whilst at University. I'm starting to focus on my own company Left Logic. It's a web development company with strong focus in usability, accessibility, clean design and powerful bespoke applications. Remy is a DZone MVB and is not an employee of DZone and has posted 53 posts at DZone. You can read more from them at their website. View Full User Profile

Image Cross Fade Transition with jQuery

04.21.2008
| 144639 views |
  • submit to reddit

A frequent query and request I receive, and have had as a developer myself is:
“How can I fade one image into another?”.

In particular, Nathan Wrigley of pictureandword.com, needed a method which would fade one image into another on a mouse roll over event, and then slowly fade back once the mouse has moved of the image.

Image rollovers were the staple JavaScript nugget of the 90s, and for a lot of JavaScript developers I know, one of the starting points that led to their passion for the JavaScript language. Today, rollovers are a no-brainer, whether with CSS or 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)

Fade Effect Example

How to Approach the Problem

There are a few different ways in which this problem can be solved (and I’d love to hear alternative methods via the comments).

Here are the different approaches I’m going to go through:

  1. Two Image
  2. Single Image
  3. 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 targeted 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 width 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 JavaScript add the sugar to the markup - in that we should really only want an image tag, and using some method, know which 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:

  1. Wrap the image in a span
  2. Insert a new image, whose source is the background-image of our start image
  3. Position the new image so that sits directly behind the starting image
  4. Bind the 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 currently only available in Safari 3 (and WebKit). However, this is a great example of how to the leverage CSS using an iPhone, in place 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.

Download the full plugin

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.

References
Published at DZone with permission of Remy Sharp, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Ferdia O'brien replied on Mon, 2010/11/01 - 4:15am

Looks like the latest versions of IE doesnt like your code. Real browsers like Firefox or Chrome of course handle it perfectly, and IE's compatibility view also manages, but without compatibility view it doesn't overlay the images properly. I hope you see this and can figure it out. I'd love to use your code, it's by far the simplest jquery rollover I've ever seen, but unfortunately people are still uninformed about what a real web browser is :(

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.