Image Cross Fade Transition

11Apr

Background

A frequent query and request I receive (and have myself) as a developer is: "how can I fade one image in to another?".

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)

Fade Effect Example

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:

  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 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:

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

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.

Coda Popup Bubbles

3Mar

Background

Coda is one of the new web development tools for the Mac - and it's popular amongst designers and developers I know. Panic (the developers of Coda) are also known for their sharp design.

In particular, Jorge Mesa writes to ask how to re-create their 'puff' popup bubble shown when you mouse over the download image.

In essence the effect is just a simple combination of effect, but there's a few nuances to be wary of.

Coda Bubble

How to Solve the Problem

To create the puff popup bubble effect, we need the following:

  1. Markup that assumes that JavaScript is disabled. It would be fair to say that the popup would be hidden from the CSS.
  2. The hidden popup, correctly styled for when we make it appear.
  3. jQuery to animate the puff effect on mouseover and mouseout.

The biggest trick to be wary of is: when you move the mouse over the popup, this triggers a mouseout on the image used to trigger the popup being shown. I'll explain (carefully) how to make sure the effect doesn't fail in this situation.

I’ve provided a screencast to walk through how create this functionality. Details on how and what I used can be found below.

Watch the coda bubble screencast (alternative flash version)

(QuickTime version is approx. 23Mb, flash version is streaming)

View the demo and source code used in the screencast

HTML Markup

For the purpose of reusability, I've wrapped my 'target' and 'popup' in a div. The target is the element which the user must mouseover to show the popup.

<div class="bubbleInfo">
  <img class="trigger" src="http://mysite.com/path/to/image.png" />
  <div class="popup">
    <!-- your information content -->
  </div>
</div>

CSS

There's very little to the minimum required CSS. Of course, how you markup your bubble will change this, and the screencast uses the version from the Coda web site, so there's a considerable amount of CSS to style the bubble.

The minimum I recommend for the example is:

.bubbleInfo {
    position: relative;
}

.popup {
    position: absolute;
    display: none; /* keeps the popup hidden if no JS available */
}

This way we can absolutely position the popup against the trigger.

jQuery

To create the effect, we need to run the following animation on the popup element:

Mouse Over

  1. On mouseover: reset the position of the popup (required because we're floating upwards as we puff away).
  2. Animate the popup's opacity from 0 to 1 and move it's CSS top position by negative 10px (to move upwards).
  3. If the mouseover is fired again, and we're still animating - ignore.
  4. If the mouseover is fired again, and the popup is already visible - ignore.

Mouse Out

  1. Set a timer to trigger the popup hide function (this prevents accidentally moving out of the 'active' area).
  2. If a timer is set (to hide), reset the timer (thus only allowing one hide function to fire).
  3. Once timed out, animiate the popup's opacity from 1 to 0 and move it's CSS top position by negative 10px (to float upwards away).
  4. Set the appropriate flags to indicate the popup is now hidden.

The 'Trick'

There was one piece of tricky logic that initially I couldn't work out. Each time I moved the mouse over the popup, it would fire a mouseout on the trigger element - which would hide the popup. This is an undesirable bug.

There may be a another way around this, and from what I can tell, the Coda site developers didn't solve it this way - but here's the solution:

You need to clear the timer set in the mouseout (point 1 above) in the mouseover. This completely solves the problem.

Complete Source Code

Here's the complete source code for the effect, including comments throughout the code to explain what each block is doing.

$(function () {
  $('.bubbleInfo').each(function () {
    // options
    var distance = 10;
    var time = 250;
    var hideDelay = 500;

    var hideDelayTimer = null;

    // tracker
    var beingShown = false;
    var shown = false;
    
    var trigger = $('.trigger', this);
    var popup = $('.popup', this).css('opacity', 0);

    // set the mouseover and mouseout on both element
    $([trigger.get(0), popup.get(0)]).mouseover(function () {
      // stops the hide event if we move from the trigger to the popup element
      if (hideDelayTimer) clearTimeout(hideDelayTimer);

      // don't trigger the animation again if we're being shown, or already visible
      if (beingShown || shown) {
        return;
      } else {
        beingShown = true;

        // reset position of popup box
        popup.css({
          top: -100,
          left: -33,
          display: 'block' // brings the popup back in to view
        })

        // (we're using chaining on the popup) now animate it's opacity and position
        .animate({
          top: '-=' + distance + 'px',
          opacity: 1
        }, time, 'swing', function() {
          // once the animation is complete, set the tracker variables
          beingShown = false;
          shown = true;
        });
      }
    }).mouseout(function () {
      // reset the timer if we get fired again - avoids double animations
      if (hideDelayTimer) clearTimeout(hideDelayTimer);
      
      // store the timer so that it can be cleared in the mouseover if required
      hideDelayTimer = setTimeout(function () {
        hideDelayTimer = null;
        popup.animate({
          top: '-=' + distance + 'px',
          opacity: 0
        }, time, 'swing', function () {
          // once the animate is complete, set the tracker variables
          shown = false;
          // hide the popup entirely after the effect (opacity alone doesn't do the job)
          popup.css('display', 'none');
        });
      }, hideDelay);
    });
  });
});

Taking it Further

This effect could be perfected by changing the initial reset (popup.css()) code to read from the trigger element and approximate it's position. In my example, I've hardcoded it because I only have one on the page - but you may want to use this effect several times across your page.

Slider Gallery

18Feb

Background

Michiel Kenis requested a tutorial explaining how to create a similar effect used to showcase the products on the Apple web site.

This 'product slider' is similar to a straight forward gallery, except that there is a slider to navigate the items, i.e. the bit the user controls to view the items. Simple stuff.

jQuery already has the plugins to create these effects so we don't have to go about creating them ourselves from scratch.

How to Solve the Problem

To create the slider controlled gallery, we need the following:

  1. Markup that allows us to place several items within a list, and that overflows out of sight.
  2. A slider widget to control what subsection of products we can see.
  3. Logic to translate the sliders position to slide the products inversely (i.e. we slide left-to-right, the products go right-to-left).

I've provided a screencast to walk through how create this functionality. Details on how and what I used can be found below.

Watch the slider gallery screencast (alternative flash version)

View the demo and source code used in the screencast

jQuery UI

jQuery UI is "a set of themable widgets and interactions". For this task, we don't need much of jQuery UI, but for the UI slider widget, we'll need the following:

  • jquery.dimensions.js
  • ui.mouse.js
  • ui.slider.js

Of course we'll still need the latest jquery.js.

All the above scripts can be extracted from the jQuery UI download.

HTML Markup

The markup for the example is straight forward. It boils down to a ul holding the items you want in the gallery, and a div with another nested div, for the slider and it's handle.

<div class="sliderGallery">
  <ul class="items">
    <li>Item one</li>
    <li>Item two</li>
    <li>Item three, etc...</li>
  </ul>
  <div class="slider">
    <!-- the handler to action the slide -->
    <div class="handle"></div>
    <!-- labels appear against the slider, as pointers to the user -->
    <span class="slider-lb1">slider label 1</span>
    <span class="slider-lb2">slider label 2</span>
    <span class="slider-lb3">slider label 3</span>
  </div>
</div>

CSS

One key feature about the CSS used: in my example, I assumed that I would not have a predefined width to the ul element. As such, I can't float the li elements and have set widths. To ensure everything flows horizontally, I make use of white-space: nowrap.

I've only included the required CSS to create the sliding gallery effect. If you want to see the complete CSS used in the screencast just view the source for the demo

Slider Gallery Container

The overflow ensures the items are hidden, and the position: relative is used to absolutely position the ul element within it.

.sliderGallery {
    overflow: hidden;
    position: relative;
    padding: 10px;
    height: 160px;
    width: 960px;
}

Slider Gallery Items

The white-space: nowrap is what allows us to work with an unknown width in the ul element.

.sliderGallery UL {
    position: absolute;
    list-style: none;
    overflow: none;
    white-space: nowrap;
    padding: 0;
    margin: 0;
}

.sliderGallery UL LI {
    display: inline;
}

Slider Handle

In the screencast, the example uses a background image for the slider handle, and in particular from the example, the width of the handle is larger than the image to allow it to overlay on the left and right arrows.

.handle {
    position: absolute;
    cursor: move;
    top: 0;
    z-index: 100;
    /* bespoke to your own solution */
    height: 17px;
    width: 181px;
}

jQuery

As stated above, we will need to include a series of plugins from the jQuery UI download, then we will add our dash of jQuery to create the sliding gallery effect.

Required Plugins

<script src="jquery.js" type="text/javascript"></script>
  
<script src="jquery.dimensions.js" type="text/javascript"></script>
<script src="ui.mouse.js" type="text/javascript"></script>
<script src="ui.slider.js" type="text/javascript"></script>

Slider Code

$(function () { // see note below
$(window).ready(function () {
  $('div.sliderGallery').each(function () {
    var ul = $('ul', this);
    var productWidth = ul.innerWidth() - $(this).outerWidth();

    var slider = $('.slider', this).slider({ 
      handle: '.handle',
      minValue: 0, 
      maxValue: productWidth, 
      slide: function (ev, ui) {
        ul.css('left', '-' + ui.value + 'px');
      }, 
      stop: function (ev, ui) {
        ul.animate({ 'left' : '-' + ui.value + 'px' }, 500, 'linear');
      }
    });
  });
});

Please note during the screencast I used $(function () {}) - but I've had to since change this to $(window).ready. This is because I'm using white-space: nowrap and the width isn't calculated until after the images are loaded. jQuery's document ready function fires before the images are loaded - to allow the JavaScript access to the DOM as early as possible.

This wasn't a problem in the demo because the images were loaded from a local server, and thus loaded fast enough to allow the code to work out the width correctly.

The code breaks down as follows:

window.onload = function () {

When the DOM and images are ready (note that if you don't use white-space: nowrap then you should use the standard $(document).ready() method).

$('div.sliderGallery').each(function () {

Loop through all the div.sliderGallery elements. This code design sets us up to convert our code to a reusable plugin.

var ul = $('ul', this);

Search for the ul in the context of this - which is currently a div.sliderGallery.

var productWidth = ul.innerWidth() - $(this).outerWidth();

.innerWidth() and .outerWidth() come from jquery.dimensions.js. We're subtracting the current div.sliderGallery width because when we slide, we want it to stop once the last item is visible, rather than sliding it all the way left until it's out of sight.

var slider = $('.slider', this).slider({ 

.slider() comes from ui.slider.js.

handle: '.handle',

The class of our slider handle.

slide: function (ev, ui) {
  ul.css('left', '-' + ui.value + 'px');
},

As the handle is moved, we move the ul of items, note that it moves negatively, creating the scrolling effect.

stop: function (ev, ui) {
  ul.animate({ 'left' : '-' + ui.value + 'px' }, 500, 'linear');
}

We use this for when slider area has been clicked, to create the scroll effect.

Taking it Further

When I have a working version of the code, I often try to think how the interaction could be better. Here's a list of ways it could be improved, in no particular order - if you fancy a challenge:

  1. Better scrolling effect, in particular, fast scroll for the bulk of the animation, and slows down when it's settling on the final item.
  2. Turn it in to a plugin.
  3. The arrow can be turned in to handlers to slide the handle only part of the distance, rather than all.
  4. A nice effect the Apple version has, is the jump labels, within the slider element, change class given the distance they are from the slider handle. They appear brighter as they're closer and duller as they are further away from the handle.
  5. Anything else you can think of!

Image Loading

4Feb

Background

Daniel Mee requested a tutorial and writes:

I have a large image (500k). I have a loading gif (little rotating circle thingy from ajaxload.info). I want the animated gif to be swapped with the large image once it's loaded. The event may be on page load or may be some button onClick...

This is similar to how a LightBox would work, except Daniel wants complete control of the the load event.

This tutorial will show how to load images in the background, and once loaded handle the event and create your own response.

Basic Version

In our basic version, we will have a single div containing a loading spinner and once our large image is loaded (in the background) we will remove the spinner and insert our image.

There's a few ways to approach the loading screen, two of which are:

  1. Use a background image on the holder div, this way we can easily centre align horizontally and vertically using CSS, rather than adding extra markup.
  2. Adding a styled div in our holder div, then remove the entire block of markup when the image loads.

I've provided a screencast explaining how to achieve this (though it is based on the CSS version, also shows how to do this with a separate loading div).

Watch the jQuery basic image load screencast (alternative flash version)

View the demo and source code used in the screencast

Note that in the demonstration as I am simulating loading a slow to load image by including a script tag at the bottom of the markup. In your real world version, you obviously would not include it.

HTML Markup

The markup (segment) that we're using is extremely simple. Note, however, I've included a 'loading' class on the div. This will be manipulated in our jQuery once the image has loaded behind the scenes:

<div id="loader" class="loading"></div>

CSS

For the demo, I've set the height and width of the empty div, but also included a background image which indicates something is loading.

You can use any kind of image - I've seen applications use much larger animated gifs to simulate a loading message box.

DIV#loader {
  border: 1px solid #ccc;
  width: 500px;
  height: 500px;
}

/** 
 * While we're having the loading class set.
 * Removig it, will remove the loading message
 */
DIV#loader.loading {
  background: url(images/spinner.gif) no-repeat center center;
}

jQuery

The jQuery's job is to:

  1. Load the image in the background
  2. Hook a load event
  3. Once the image has loaded, strip the loading class and insert the image
// when the DOM is ready
$(function () {
  var img = new Image();
  
  // wrap our new image in jQuery, then:
  $(img)
    // once the image has loaded, execute this code
    .load(function () {
      // set the image hidden by default    
      $(this).hide();
    
      // with the holding div #loader, apply:
      $('#loader')
        // remove the loading class (so no background spinner), 
        .removeClass('loading')
        // then insert our image
        .append(this);
    
      // fade our image in to create a nice effect
      $(this).fadeIn();
    })
    
    // if there was an error loading the image, react accordingly
    .error(function () {
      // notify the user that the image could not be loaded
    })
    
    // *finally*, set the src attribute of the new image to our image
    .attr('src', 'images/headshot.jpg');
});

Added Functionality

The jQuery code used is the constructs of our load function. If we wanted to style the image in any way, add classes or run an Ajax request before showing the image, it would go inside the .load() function.

This functionality could also be placed inside of a .click() event handler or anything else if you wanted to trigger the image loading.

For example, if you had a particular image map linked to this image that you wanted to request via Ajax the contents of the load function would be this:

$(img)
  .load(function () {
    $(this).hide();
    
    // our bespoke ajax hit that's required with the image
    // it will return the HTML for the <map> element and
    // linked <area> element.
    $.ajax({
      url: 'image-map.php',
      data: 'img=' + i.src, // the image url links up in our fake database
      dataType: 'html',
      success: function (html) {
        // because we're inside of the success function, we must refer
        // to the image as 'img' (defined originally), rather than 'this'
        $('#loader')
          .removeClass('loading')
          .append(img)
          .append(html);
          
        // now show the image
        $(img).fadeIn();
      }
    });
  })
  // code continues as before

jQuery Tabs

18Jan

Background

Tabbing has been common place on the Internet for some time now. Today web sites will make use of tabbing without the page having to reload with the addition of JavaScript.

For this tutorial I have created two screencasts demonstrating how to quickly apply a dash of jQuery to different layouts of markup, which, if the markup is well designed, it will automatically degrade nicely without JavaScript enabled. In a future article we will cover default browser functionality (i.e. supporting back, forward and bookmark buttons) and saving tab states.

Conventional and simple markup

Watch the jQuery tabs screencast - part 1 (flash version)

(QuickTime version is approx. 25Mb, flash version is streaming)

View demo and code used in screencast

Typical tabbing structure, and the one used in the demo is usually marked up as follows:

<div class="tabs">
  <!-- tabs -->
  <ul class="tabNavigation">
    <li><a href="#message">Send a message</a></li>
    <li><a href="#shareFile">Share a file</a></li>
    <li><a href="#arrange">Arrange a meetup</a></li>
  </ul>

  <!-- tab containers -->
  <div id="message">
    <p>Lorem ipsum dolor sit amet.</p>
  </div>
  <div id="shareFile">
    <p>Sed do eiusmod tempor incididunt.</p>
  </div>
  <div id="arrange">
    <p>Ut enim ad minim veniam</p>
  </div>
</div>

Here's the jQuery used, though slightly adapted from this screencast:

$(function () {
    var tabContainers = $('div.tabs > div');
    
    $('div.tabs ul.tabNavigation a').click(function () {
        tabContainers.hide().filter(this.hash).show();
        
        $('div.tabs ul.tabNavigation a').removeClass('selected');
        $(this).addClass('selected');
        
        return false;
    }).filter(':first').click();
});

Real World example

Watch the jQuery tabs screencast - part 2 (flash version)

(QuickTime version is approx. 30Mb, flash version is streaming)

View demo and code used in screencast

Chris Mahon requested a tutorial that explained how to apply tabbing to markup that doesn't follow the above markup convention.

In his case, the tabs were in a different DIV to the tab containers, in addition, one item in the UL wasn't a tab, but used to link to a different page.

Here's the jQuery tab code used:

$(function () {
    var tabs = [];
    var tabContainers = [];
    $('ul.tabs a').each(function () {
      // note that this only compares the pathname, not the entire url
      // which actually may be required for a more terse solution.
        if (this.pathname == window.location.pathname) {
            tabs.push(this);
            tabContainers.push($(this.hash).get(0));
        }
    });
    
    $(tabs).click(function () {
        // hide all tabs
        $(tabContainers).hide().filter(this.hash).show();
        
        // set up the selected class
        $(tabs).removeClass('selected');
        $(this).addClass('selected');
        
        return false;
    });
});

Taking it further

These screencasts are really designed to help you understand the thought process and how to go about writing the code for the tabs.

In a future article I will explain how to convert this code to a plugin that can be reused on your projects, and what to watch out for when making the code reusable.

Of course, you can always use the jUI Tabs to accomplish the task if you require more dynamic functionality.

Slide out and drawer effect

6Jan

Background

John Resig, author of jQuery demonstrated how easy is create this simple slide aka drawer aka accordion effect during @media Ajax 2007 - it also makes for a great introduction to jQuery.

The effect is commonly known as an 'accordion' and it's usually used to slide up, or down blocks of content to expose new blocks.

Apple downloads drawer sample

The Apple web site is a great demonstration of this effect in action, where the mouse settles on the title of the 'section' and the associated links are exposed. What makes this effect particularly cool, is that the drawers maintain a fixed height and slide between restricted area.

This tutorial will walk through how to create your own simple plugin, and then replicate the Apple downloads drawers using the very excellent Accordion plugin.

Markup

Since we're practising unobtrusive JavaScript, the markup will start with headings separating the list items.

There's two approaches depending on your content. Either to use list elements or definition lists. Our example will use list elements.

<ul class="drawers">
  <li class="drawer">
    <h2 class="drawer-handle open">Downloads</h2>
      <ul>
        <!-- list items -->

And so on, with as many drawer elements as we want. Note that in this example, the 'open' class has only been applied to the first drawer.

Simple jQuery slide effect

In it's simplest form, the effect is this when the user clicks on the drawer handle:

  1. Hide all the drawer content elements
  2. Show the drawer content associated with the current drawer handle

In addition, when the page loads, we should hide all the drawer content, except for our starting point.

This is achieved with the following code:

$(document).ready(function () {
  // hide all ULs inside LI.drawer except the first one
  $('LI.drawer UL:not(:first)').hide(); 
  
  // apply the open class
  $('LI.drawer UL:first').addClass('open');
  
  $('H2.drawer-handle').click(function () {
    // hide the currently visible drawer contents
    $('LI.drawer UL:visible').hide();
    
    // remove the open class from the currently open drawer
    $('H2.open').removeClass('open');
    
    // show the associated drawer content to 'this' (this is the current H2 element)
    // since the drawer content is the next element after the clicked H2, we find
    // it and show it using this:
    $(this).next().show();
    
    // set a class indicating on the H2 that the drawer is open
    $(this).addClass('open');
  });
});

If you wanted to make better using of jQuery's chaining the code could be written as this:

$(function () { // same as $(document).ready(function () { })
  // assuming we have the open class set on the H2 when the HTML is delivered
  $('LI.drawer H2:not(.open)').next().hide();

  $('H2.drawer-handle').click(function () {
    // find the open drawer, remove the class, move to the UL following it and hide it
    $('H2.open').removeClass('open').next().hide();
    
    // add the open class to this H2, move to the next element (the UL) and show it
    $(this).addClass('open').next().show();
  });
});

If you want it to slide, we can easily swap .hide() for .slideUp() and .show() for .slideDown(), as I have done in the working example of the simple slide demo

If you look at the demo, for me, the annoying aspect is that the height changes depending on which drawer you've got open. In particular, if you have the first drawer open, and click on the second, the position of the third drawer handle moves up.

That's where the accordion plugin helps us.

jQuery accordion plugin

Using the same markup as the previous example, if we take Jörn Zaefferer's Accordion plugin we can match the effect on Apple's site.

Before we can use the plugin however, there are a few prerequisites: in particular, you must have the dimensions plugin. The best place to get the latest files is the plugin repository for Accordion. Download the zip file (using the link towards the foot of the page) and grab the following files:

  • jquery.accordion.js (or the packed versions)
  • lib/jquery.js (if you've not got the library already)
  • lib/jquery.dimensions.js

Again, using our existing markup, we can use the following code to get the plugin working straight away:

<script src="jquery.js" type="text/javascript"></script>  
<script src="jquery.dimensions.js" type="text/javascript"></script>  
<script src="jquery.accordion.js" type="text/javascript"></script>
<script type="text/javascript">
<!--
$(function () {
  $('UL.drawers').accordion({
    // the drawer handle
    header: 'H2.drawer-handle',
    
    // our selected class
    selectedClass: 'open',
    
    // match the Apple slide out effect
    event: 'mouseover'
  });
});
-->
</script>

That's it! Simple and very easy to use so long as you have all the right files.

See the plugin version of the slide out effect

Using Ajax to Validate Forms

18Dec

Background

Forms are such a common element on the Internet we tend to blunder through them without too much thought. However, if the web site has added a few nice touches that make the process easier, it tends to speed up the process and reduce any frustration in finding our preferred username (i.e. try getting your name under Hotmail!).

There's two types of validation:

  1. Simple, server-independent validation, i.e. checking email address are the right format, password and confirm password matches, etc.
  2. Server-based validation, with can be delivered with a page refresh, or live (as this tutorial will explain), i.e. for checking whether a username is taken.

What can we do?

  • Live username checking
  • Password confirmation and strength
  • Checking if an email address is already registered
  • URL validation, i.e. Basecamp's site address checks if the URL is available (pretty much the same as username validation)

So why do it?

  • It makes the user's experience quicker, or arguably better
  • It's not much extra work to put in place (explained below)

Examples in the wild

Howto

As with all these tutorials, I expect that you have built your solution to the point where it works, but now we want to add our splash of JavaScript magic.

In our baseline example, my requirements are:

  • Username validation kept in separate function
  • Server side does the it's normal job
  • I can detect an Ajax request and return something different

Username validation

Our PHP function to validate the username reads:

function check_username($username) {
  $username = trim($username); // strip any white space
  $response = array(); // our response
  
  // if the username is blank
  if (!$username) {
    $response = array(
      'ok' => false, 
      'msg' => "Please specify a username");
      
  // if the username does not match a-z or '.', '-', '_' then it's not valid
  } else if (!preg_match('/^[a-z0-9\.\-_]+$/', $username)) {
    $response = array(
      'ok' => false, 
      'msg' => "Your username can only contain alphanumerics and period, dash and underscore (.-_)");
      
  // this would live in an external library just to check if the username is taken
  } else if (username_taken($username)) {
    $response = array(
      'ok' => false, 
      'msg' => "The selected username is not available");
      
  // it's all good
  } else {
    $response = array(
      'ok' => true, 
      'msg' => "This username is free");
  }

  return $response;        
}

This format for a response is good, because we are using it to display error messages on the page, but we can also convert it to JSON and use it in our Ajax response later on.

Markup

Again, it's assumed your markup is already designed to show error messages.

For this example the following markup is being used within a fieldset:

<div>
    <label for="username">Username, valid: a-z.-_</label>
    <input type="text" name="username" value="<?=@$_REQUEST['username']?>" id="username" />
    <span id="validateUsername"><?php if ($error) { echo $error['msg']; } ?></span>
</div>

jQuery

Our client side check will perform the following:

  1. Only if the value has changed run the check, i.e. ignore meta keys
  2. Use a nice ajax spinner to indicate activity
  3. Make an Ajax request and show the response
$(document).ready(function () {
  var validateUsername = $('#validateUsername');
  $('#username').keyup(function () {
    var t = this; 
    if (this.value != this.lastValue) {
      if (this.timer) clearTimeout(this.timer);
      validateUsername.removeClass('error').html('<img src="images/ajax-loader.gif" height="16" width="16" /> checking availability...');
      
      this.timer = setTimeout(function () {
        $.ajax({
          url: 'ajax-validation.php',
          data: 'action=check_username&username=' + t.value,
          dataType: 'json',
          type: 'post',
          success: function (j) {
            validateUsername.html(j.msg);
          }
        });
      }, 200);
      
      this.lastValue = this.value;
    }
  });
});

The code breaks down as follows:

$(document).ready(function () {

Run the JavaScript in this (anonymous) function when the document has loaded.

var validateUsername = $('#validateUsername');

Create a cached copy of the validateUsername span because it will help a little with optimisation (it's a good practise to have).

$('#username').keyup(function () {

Run the JavaScript in this (anonymous) function on key up.

var t = this;

Cache the 'this' instance as we need access to it within a setTimeout, where 'this' is set to 'window', which can cause all manners of confusion.

if (this.value != this.lastValue) {

Only run the check if the username has actually changed - also means we skip meta keys.

if (this.timer) clearTimeout(this.timer);

The timeout logic means the ajax doesn't fire with every key press, i.e. if the user holds down a particular key, it will only fire when the release the key.

validateUsername.removeClass('error').html('<img src="images/ajax-loader.gif" height="16" width="16" /> checking availability...');

Show our holding text in the validation message space.

this.timer = setTimeout(function () {

Fire an ajax request in 1/5 of a second.

$.ajax({
  url: 'ajax-validation.php',
  data: 'action=check_username&username=' + t.value,
  dataType: 'json',
  type: 'post',
  success: function (j) {
    validateUsername.html(j.msg);
  }
});

The actual Ajax request. If the script ajax-validation.php returns any response, convert it to JSON and put the 'msg' field in to the validation message.

The fields are set in the PHP server response. You can see this working in the demo, and view the source, in particular look at the $resp array from check_username.

this.lastValue = this.value;

Finally, put the current value in to a cache, to make sure we ignore meta keys (from above).

Server response

Now all I have to add to the PHP code, that already handles normal username validation is:

if (@$_REQUEST['action'] == 'check_username' && isset($_SERVER['HTTP_X_REQUESTED_WITH'])) {
    echo json_encode(check_username($_REQUEST['username']));
    exit; // only print out the json version of the response
}

So long as this is high enough in the logic, it will just check the username is valid, using our existing function and convert the response to a JSON object (you may need to add JSON support for PHP, otherwise it's bundled with PHP 5.2.0).

Example & taking it further

In the example I've provided, I've also shown how we can add JavaScript to a normally coded page to also validate the user providing an avatar URL.

It's all about CSS

6Dec

If you've got a good understand of CSS selectors, then you're already familiar with how to query the DOM.

Background

CSS1

jQuery uses CSS1, 2 and parts of CSS3 at the heart of all it's queries, just in the same way you would use CSS to target an element or group of elements to style.

In CSS, to hide an element we could use:

.hidden { display: none; }

Using jQuery we can achieve the same effect by finding all the elements whose class is hidden and hiding them like this:

$('.hidden').hide();

Equally to target an ID, in CSS we use:

#header { display: none; }

Which is this in jQuery:

$('#header').hide();

CSS2

More complicated CSS selectors can be used using CSS2. Here we are styling all links that have an attribute of type="application/pdf" with a nice little icon, and doing something cool when they click on the link.

a[type="application/pdf"] { 
  background-image: url(/images/pdf.gif);
  padding-left: 20px;
}
$('a[type="application/pdf"]').click(doSomethingCool);

CSS3

Taking the above example further with CSS3 is easy. Let's assume the markup already exists, and we want to find all the anchors that link to pdfs:

a[href$=".pdf"] { 
  background-image: url(/images/pdf.gif);
  padding-left: 20px;
}
$('a[href$=".pdf"]').click(doSomethingCool);

You should check the full jQuery selector documentation for which CSS3 selectors are supported.

If your page has a lot of markup, try to use context to narrow down the selector searching. Particularly, i.e. $('.hidden') can be slow if you have a 1,000 elements on the page.

Example

So you've got your built web page, fully styled and there's no interaction layered on yet. Using these CSS selectors we can target elements and hook actions.

In this simple example we have a registration form. We have terms and conditions that if the user wants to read, they'll slide out to be shown.

The terms are both linked to and hidden on the page. If the user clicks on the link and JavaScript is enabled, we'll handle the click action ourselves by sliding the text in to view.

$(document).ready(function () { // when the DOM is ready do:
    $('A.reveal').click(function () {
        $('#terms').slideDown();
        return false; // don't follow the default link action
    });
});

This code breaks down as follows:

$(document).ready(function () {

Run the JavaScript in this (anonymous) function when the document has loaded.

$('A.reveal').click(function () {

Find all the anchors with the class 'reveal' and hook a click event.

$('#terms').slideDown();

When the user clicks, find the element whose id is 'terms' and fire the slideDown animation. You should note that these effects are smart enough to know whether the target element is already shown or not. So in this particular example, if the terms element is already being displayed, it won't try to slide it down on clicking again.

return false;

Finally, this stops the browser following the link's href (which is the default action).

Here's the working example

Taking it further

The best way to understand the selectors and how they work is by injecting jQuery (using the bookmarklet) in to a page and testing out selecting using firebug.