|
Partners
|
Preloading images and executing code only after all images have loadedDate created: May 23rd, 2011 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 newimages=[]
var arr=(typeof arr!="object")? [arr] : arr //force arr parameter to always be an array
for (var i=0; i<arr.length; i++){
newimages[i]=new Image()
newimages[i].src=arr[i]
}
}
//preload 3 images:
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 preloadimages(['1.gif', '2.gif', '3.gif']).done(function(images){
//code inside here is run after all images have preloaded
//images parameter is an array of image objects corresponding to the preloaded images
//images[0] references the 1st image etc
})
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 handlersTo detect when an image has fully loaded or failed to load,
we turn to the function preloadimages(arr){
var newimages=[], loadedimages=0
var arr=(typeof arr!="object")? [arr] : arr
function imageloadpost(){
loadedimages++
if (loadedimages==arr.length){
alert("All images have loaded (or died trying)!")
}
}
for (var i=0; i<arr.length; i++){
newimages[i]=new Image()
newimages[i].src=arr[i]
newimages[i].onload=function(){
imageloadpost()
}
newimages[i].onerror=function(){
imageloadpost()
}
}
}
//sample run
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 Adding callback functionality to our preloadimages() functionOk, now that we have the first half of our ultimate goal
completed, time to move on to the second part of modifying preloadimages(imagesarray, function(){
//code to run when images have loaded
})
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(){
//code to run when images have loaded
})
In other words, we chain another function function preloadimages(arr){
var newimages=[], loadedimages=0
var postaction=function(){}
var arr=(typeof arr!="object")? [arr] : arr
function imageloadpost(){
loadedimages++
if (loadedimages==arr.length){
postaction(newimages) //call postaction and pass in newimages array as parameter
}
}
for (var i=0; i<arr.length; i++){
newimages[i]=new Image()
newimages[i].src=arr[i]
newimages[i].onload=function(){
imageloadpost()
}
newimages[i].onerror=function(){
imageloadpost()
}
}
return { //return blank object with done() method
done:function(f){
postaction=f || postaction //remember user defined callback functions to be called when images load
}
}
}
preloadimages(['1.gif', '2.gif', '3.gif']).done(function(images){
//call back codes, for example:
alert(images.length) //alerts 3
alert(images[0].src+" "+images[0].width) //alerts '1.gif 220'
})
Lets go over the changes now:
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){
return a.width-b.width //sort images by each image's width property, ascending
})
alert(images[0].src) //alerts the src of the smallest image width wise
})
Cool! |