Google analytics code

Thursday, January 3, 2008

Javascript: Image Preloader

I've been working on an image gallery program in jQuery for a little while. The biggest problem I ran into was trying to preload the first two images before the show started. I experimented with a few different methods, but couldn't find something that worked well across all browsers.

I was looking through mootools one day and found out they have a built in image preloader using Asset.images. It works well enough, but I'm not a big fan of mootools. Plus I wanted something that could plug into any library. After a bit of testing I came up with something that has worked really well across Safari 1.x & 2.x, Firefox 1.x & 2.x, IE 6, 7 & 8 and Opera.

It's a class built with objects instead of prototypes. I prefer objects now because it keeps everything in a nice neat package instead of having variables and functions scattered all over the place. I based a lot of the script on the Images Asset of mootools and the New Wave style John Resig talks about in Pro Javascript Techniques.

preloadImages = 
{
  count: 0 /* keep track of the number of images */
  ,loaded: 0 /* keeps track of how many images have loaded */
  ,onComplete: function(){} /* fires when all images have finished loadng */
  ,onLoaded: function(){} /*fires when an image finishes loading*/
  ,loaded_image: "" /*access what has just been loaded*/
  ,images: [] /*keeps an array of images that are loaded*/
  ,incoming:[] /*this is for the process queue.*/
  /* this will pass the list of images to the loader*/
  ,queue_images: function(images)
  {
    //make sure to reset the counters
    this.loaded = 0;
    
    //reset the images array also
    this.images = [];
    
    //record the number of images
    this.count = images.length;
    
    //store the image names
    this.incoming = images;
    
    //start processing the images one by one
    this.process_queue();
  }
  ,process_queue: function()
  {
    //pull the next image off the top and load it
    this.load_image(this.incoming.shift());
  }
  /* this will load the images through the browser */
  ,load_image: function(image)
  {
    var this_ref = this;
    var preload_image = new Image;
    
    preload_image.onload = function()
    {
      //store the loaded image so we can access the new info
      this_ref.loaded_image = preload_image;
      
      //push images onto the stack
      this_ref.images.push(preload_image);
      
      //note that the image loaded
      this_ref.loaded +=1;
      
      //fire the onloaded
      (this_ref.onLoaded)();
      
      //if all images have been loaded launch the call back
      if(this_ref.count == this_ref.loaded)
      {
        (this_ref.onComplete)(); 
      }
      //load the next image
      else
      {
        this_ref.process_queue();
      }
    }
    preload_image.src = image;
  }
}

Everything is stored in the preloadImages object. There is only one namespace so it shouldn't interfere with any of your other code. You can store this in any script you write, or include it from an external file.

The one thing I really like about this script is the image loading is sequential. One big problem I ran into was images loading out of order. Firefox would grab the images in the order listed by the array. Internet Explorer would grab everything, but not return it in the right order. This script grabs one image at a time and makes sure the order is preserved. This is important for scripts like slide shows with labels for each image.

To begin preloading images use the method preloadImages.queue_images([IMAGES]) where [IMAGES] is an array of images. Everything will be processed internally. There are two methods you can use: onLoaded and onComplete.

onLoaded will fire whenever an image has finished loading. All the properties of the loaded image are available in the variable loaded_image. For example if you want to find the width of the image that just finished loading your code would look like this.
preloadImages.onLoaded = function()
{
  alert(preloadImages.loaded_image.width);
}
preloadImages.queue_images(['new_404.jpg']);
A pop up window would appear and display the width of the image that just loaded. All the properties of and image will be available through the variable. onLoaded must appear before queue_images to fire properly.

When an image is loaded a reference is stored in an array named images. This is useful if you want to load all your images at once, then loop back through them for past information.

onComplete will fire when all the images have loaded. I tried to use the onerror method, but it doesn't work well across all browsers and caused more frustration then it was worth. Like onLoaded, onComplete must come before queue_images to fire properly. Here as an example of how to use onComplete.

preloadImages.onComplete = function()
{
  alert('All Done');
}
preloadImages.queue_images(['new_404.jpg']);

It's a pretty simple script. It can be used in combination with lots of other scripts. There are a lot of comments in the script so it's pretty easy to follow. If you have any questions about it feel free to email me.