Image Fade Revisited

Posted on 28th July 2008 — This episode is revisiting the image cross fade effect, in particular Dragon Interactive has a beautiful little transition for their navigation that some readers have been requesting. Greg Johnson takes it one step further to implement this method using jQuery and the methods shown here.

Watch the image fade revisited screencast (alternative flash version)

QuickTime version is approximately 30Mb, flash version is streaming.

View the demo and source code used in the screencast

Also a side note about IE6 support:

Side note: IE6 screencast (alternative flash version)

Understanding the Problem

So let’s break this down and see how it’s done.

The first thing I look for when understanding how a web effect has been achieved, is to look at what they’ve done when JavaScript is disabled. Where possible, I always want to produce a solution that works cleanly without JavaScript enabled.

Since Dragon Interactive are using the effect for navigation, as I suspected, the effect degrades to…nothing. Although we’ll take it one step further by creating a hover state for the navigation.

The second thing I’ll look at is the difference between the sent markup and the rendered markup. This gives me the clues I need to understand what’s going on behind the scenes without having to read their JavaScript.

The key in their version is there is a new span within the hover area that isn’t in the sent markup. So this is what’s being created using JavaScript, and this is also the key to the whole effect.

From this, we can see when the anchor is hovered over, the newly created span’s opacity is faded up and completes the transition effect.


Note that our default HTML has this extra class="highlight" because I want the hover effect to still trigger if JavaScript is disabled. We’ll strip out this class in the jQuery later on. It also means we’ll have slightly more CSS to support both the hover state and the jQuery based hover state.

Also, everything with the anchor will be hidden due to the #navigation a * { display: none; } rule. This means with CSS turned off the navigation still works.

<ul id="navigation">
  <li class="highlight"><a href="#" class="home"><span>Home</span></a></li>
  <li class="highlight"><a href="#" class="services"><span>Services</span></a></li>
  <li class="highlight"><a href="#" class="portfolio"><span>Portfolio</span></a></li>
  <li class="highlight"><a href="#" class="about"><span>About</span></a></li>
  <li class="highlight"><a href="#" class="contact"><span>Contact</span></a></li>


This is only a snippet of the CSS (for full CSS see the working example). We’re using a CSS sprite and changing the background position both in the a:hover state and for the jQuery create span.

#navigation a * {
  display: none;

#navigation a,
#navigation a .hover {
  height: 70px;
  position: relative;
  display: block;
  background: url(images/dragon-sprite.jpg) 0 0 no-repeat;

#navigation a.home {
  background-position: 0 0;
  width: 102px;

/* .highlight will be remove if JS is enabled */
#navigation .highlight a.home:hover, 
#navigation a.home .hover {
  background-position: 0 -280px;
  width: 102px;


Finally, all that’s left is to create the new span and fade it up and down when we hover over.

$(function () {
  // IE6 doesn't handle the fade effect very well - so we'll stick with
  // the default non JavaScript version if that is the user's browser.
  if ($.browser.msie && $.browser.version < 7) return;
  $('#navigation li')
    // remove the 'highlight' class from the li therefore stripping 
    // the :hover rule
    // within the context of the li element, find the a elements
    // create our new span.hover and loop through anchor:
    .append('<span class="hover" />').each(function () {
      // cache a copy of the span, at the same time changing the opacity
      // to zero in preparation of the page being loaded
      var $span = $('> span.hover', this).css('opacity', 0);
      // when the user hovers in and out of the anchor
      $(this).hover(function () {
        // on hover
        // stop any animations currently running, and fade to opacity: 1
        $span.stop().fadeTo(500, 1);
      }, function () {
        // off hover
        // again, stop any animations currently running, and fade out
        $span.stop().fadeTo(500, 0);