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.
Hi,
ReplyDeleteI'm told 10% of web users don't have javascript enabled. Therefore it may be better to preload images using CSS.
A good example of this can be found from the URL below.
http://www.prismgraphicdesign.co.uk/blog/?p=12
Regards,
Tony
This works great.
DeleteThanks!
It seems whenever you're preloading images you're probably doing something that requires javascript, but that's not always true. Using CSS to load images definitely has it's place. A easy example is the icon bar in TinyMCE (http://tinymce.moxiecode.com/). It uses one large image that has all the icons for the editor bar and clips them with css properties.
ReplyDeleteI like being able to control the order in which images load. If you're creating a image gallery that assumes the user doesn't have javascript I wonder if there is much benefit to preloading the images if the user goes to another page before everything is done.
Just pointing out that although css maybe is suited in some solutions, some cases require js image loading.
ReplyDeleteI've modified this script a bit to parallel download the images: take a look:
http://blog.lieldulev.com/2010/05/21/parallel-image-preloading-in-js/
@Liel That's a pretty slick script. When I tried loading everything at once it kept running into problems in IE where images would only partially load before going to the next one. I'll have to play with this one with I have some time.
ReplyDelete