Preloading images is one of those time tested JavaScript
techniques that remain popular even today for loading up images as soon
as possible in the background. The idea is to kick start the process as
soon as the page begins to load so that when the application in
question needs to display them, such as an image rollover effect, those
images will hopefully have preloaded already and be shown instantly. The
following simple function preloads any number of images when called:
function preloadimages(arr){ |
var arr=( typeof arr!= "object" )? [arr] : arr |
for ( var i=0; i<arr.length; i++){ |
preloadimages([ '1.gif' , '2.gif' , '3.gif' ]) |
For
the most part this technique suffices in giving our desired images a
head start in terms of their loading, as that's all we're looking for, a
head start. Sometimes though, we need more than that- we need an actual
guarantee that our images have preloaded fully before accessing them.
Consider a photo slideshow where each image should be centered within
the outer container when shown. To do this we need access to each
image's dimensions to properly orient them, which can only be done
reliably when the image has completely loaded. Is it the end of the
world if the images aren't always centered? No, but it certainly does
ruin the effect we so painstakingly added to differentiate our slideshow
from the others. Luckily getting JavaScript to acknowledge when a group
of images have actually preloaded isn't difficult. The trick is to
utilize the onload
and onerror
event handlers
of JavaScript's image object to detect when an image has fully loaded
(or failed at that), then run the callback function when everything is
complete. In this article we'll see how to transform our original preloadimages()
function into this:
preloadimages([ '1.gif' , '2.gif' , '3.gif' ]).done( function (images){ |
Lets break up the explanation into two parts now.
Detecting when images have loaded (or failed to load) using the image object's onload and onerror event handlers
To detect when an image has fully loaded or failed to load, we turn to the onload
and onerror
event handlers of the image object. And by incrementing a counter
whenever that happens, we can in turn find out when all images within
our collection of images have loaded. Lets modify our original preloadimages()
function now to alert a message when it's done with the preloading:
function preloadimages(arr){ |
var newimages=[], loadedimages=0 |
var arr=( typeof arr!= "object" )? [arr] : arr |
function imageloadpost(){ |
if (loadedimages==arr.length){ |
alert( "All images have loaded (or died trying)!" ) |
for ( var i=0; i<arr.length; i++){ |
newimages[i].onload= function (){ |
newimages[i].onerror= function (){ |
preloadimages([ '1.gif' , '2.gif' , '3.gif' ]) |
Our
function now alerts a message when all of the images passed into it
have finished preloading. It does this by incrementing the variable loadedimages
each time an image has either loaded or failed to load, and when that
number equals the total number of images entered into the function, we
know the entire process is complete.
Adding callback functionality to our preloadimages() function
Ok, now that we have the first half of our ultimate goal completed, time to move on to the second part of modifying preloadimages()
so it accepts a callback function that kicks in when all the images
have preloaded. The classic interface for this is just to have the
primary function accept an anonymous function as a parameter, with the
later being the callback function:
preloadimages(imagesarray, function (){ |
In
this case though, lets do things a little differently. Instead of the
above, we'll implement the following interface instead, which arguably
is more intuitive from the user's perspective:
preloadimages(imagesarray).done( function (){ |
In other words, we chain another function done()
to our main function to be the recipient of any callback code. With that in mind, here is the final modified preloadimages()
function first, then a step by step explanation on the changes:
function preloadimages(arr){ |
var newimages=[], loadedimages=0 |
var postaction= function (){} |
var arr=( typeof arr!= "object" )? [arr] : arr |
function imageloadpost(){ |
if (loadedimages==arr.length){ |
for ( var i=0; i<arr.length; i++){ |
newimages[i].onload= function (){ |
newimages[i].onerror= function (){ |
postaction=f || postaction |
preloadimages([ '1.gif' , '2.gif' , '3.gif' ]).done( function (images){ |
alert(images[0].src+ " " +images[0].width) |
Lets go over the changes now:
First, I define an empty postaction()
function that will be used later on to store any user defined callback
functions that should be run when the images are fully preloaded.
At the end of preloadimages()
it returns an empty object containing a simple method done()
. This enables done()
to be chained on top of the main function, with its purpose being to
accept and preserve the callback functions defined by the user, by
wrapping them inside an anonymous function and then overwriting the postaction()
function with it. Thanks to the power of closures, our main preloadimages()
function has access to the user defined callback functions via the postaction()
function.
Note how the anonymous function itself is indirectly passed the newimages
array so the user has access to all of the preloaded images as an array of image objects inside the done()
method.
In the below we'll use our newly constructed function to preload some images then sort them based on their widths (ascending):
preloadimages([ 'ed.jpg' , 'fei.jpg' , 'budapest.gif' , 'duck.jpg' ]).done( function (images){ |
images.sort( function (a,b){ |
Cool!
0 comments:
Post a Comment