Image preloader javascript that supports events
Solution 1:
Here's a function that will preload images from an array and call your callback when the last one has finished:
function preloadImages(srcs, imgs, callback) {
var img;
var remaining = srcs.length;
for (var i = 0; i < srcs.length; i++) {
img = new Image();
img.onload = function() {
--remaining;
if (remaining <= 0) {
callback();
}
};
img.src = srcs[i];
imgs.push(img);
}
}
// then to call it, you would use this
var imageSrcs = ["src1", "src2", "src3", "src4"];
var images = [];
preloadImages(imageSrcs, images, myFunction);
And since we're now in the age of using promises for asynchronous operations, here's a version of the above that uses promises and notifies the caller via an ES6 standard promise:
function preloadImages(srcs) {
function loadImage(src) {
return new Promise(function(resolve, reject) {
var img = new Image();
img.onload = function() {
resolve(img);
};
img.onerror = img.onabort = function() {
reject(src);
};
img.src = src;
});
}
var promises = [];
for (var i = 0; i < srcs.length; i++) {
promises.push(loadImage(srcs[i]));
}
return Promise.all(promises);
}
preloadImages(["src1", "src2", "src3", "src4"]).then(function(imgs) {
// all images are loaded now and in the array imgs
}, function(errImg) {
// at least one image failed to load
});
And, here's a version using 2015 jQuery promises:
function preloadImages(srcs) {
function loadImage(src) {
return new $.Deferred(function(def) {
var img = new Image();
img.onload = function() {
def.resolve(img);
};
img.onerror = img.onabort = function() {
def.reject(src);
};
img.src = src;
}).promise();
}
var promises = [];
for (var i = 0; i < srcs.length; i++) {
promises.push(loadImage(srcs[i]));
}
return $.when.apply($, promises).then(function() {
// return results as a simple array rather than as separate arguments
return Array.prototype.slice.call(arguments);
});
}
preloadImages(["src1", "src2", "src3", "src4"]).then(function(imgs) {
// all images are loaded now and in the array imgs
}, function(errImg) {
// at least one image failed to load
});
Solution 2:
For a more robust solution, consider this PRELOADER
function with a couple of callbacks (jsFiddle).
Keeping it simple:
In this example, I'm passing callbacks and an image hash inside an Object
literal PRELOADER_OBJECT
, then overriding the callbacks inside PRELOADER
:
// preloder object stores image hash
// and event handler callbacks
var PRELOADER_OBJECT = {
imgArray:"http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg http://torwars.com/wp-content/uploads/2012/02/chewbacca-w-han-solo-anh.jpg".split(" "),
progressCallback : function( percent )
{
$( '#preloader_progress' ).html( 'preload progress complete : ' + percent + '%' );
console.log( 'preload progress complete : ', percent );
},
completeCallback : function( scope )
{
// hide preload indicator, do something when finished
console.log( 'preload complete!' );
$( '#preloader_modal' ).delay( 1000 ).animate( { opacity : 0 }, function( )
{
$( '.preload_class' ).each( function( index )
{
$( this ).delay( index * 100 ).animate( { opacity : 0 } );
} );
} );
}
/*Localize params and create PRELOADER object.
Needs to loadImages( ); iterate through hash and
call onPreloadProgress( ) and onPreloadComplete( )
each time until finished. If you're still within
bounds of the image hash, call progressCallback( )
recursively. When finished, fire onCompleteCallback( )*/
var PRELOADER = function( object )
{
// preloader modal overlay
this.modal = undefined;
// progress indicator container
this.progressIndicator = undefined;
// image preload progress
this.progress = undefined;
// progress callback
this.progressCallback = undefined;
// complete callback
this.completeCallback = undefined;
// hash to store key : value pairs for image paths
this.imgArray = undefined;
// store images in preloadArray
this.preloadArray = [];
// initialize and localize our data
this.initialize = function( )
{
// create preload indicator and overlay modal
this.createPreloaderModal( );
// image hash
this.imgArray = object.imgArray;
// progress callback event handler
this.progressCallback = object.progressCallback;
// complete callback event
this.completeCallback = object.completeCallback;
// load images
this.loadImages( );
};
this.progressCallback = function( ) {}; // function to override
this.completeCallback = function( ) {}; // function to override
// load images into DOM and fire callbacks
this.loadImages = function( )
{
var that = this;
// iterate through hash and place images into DOM
$.each( PRELOADER_OBJECT.imgArray, function( index, object )
{
this.image = $( "<img/>", { "src" : object, "class": "preload_class" } ).appendTo( 'body' );
// mark progress and call progressCallback( ) event handler
that.progress = Math.ceil( ( index / PRELOADER_OBJECT.imgArray.length ) * 100 );
that.progressCallback( this.progress );
that.preloadArray.push( this.image );
} );
// check for array bounds and call completeCallback( )
if ( PRELOADER_OBJECT.imgArray.length )
{
this.progressCallback( 100 );
this.completeCallback( this );
}
};
// create modal to display preload data
this.createPreloaderModal = function( )
{
this.modal = $( '<div/>', { 'id' : 'preloader_modal' } ).appendTo( 'body' );
this.progressIndicator = $( '<h1/>', { 'id' : 'preloader_progress' } ).appendTo( this.modal );
};
};
// trigger event chain when DOM loads
$( document ).ready( function( )
{
// instantiate PRELOADER instance and pass
// our JSON data model as a parameter
var preloader = new PRELOADER( PRELOADER_OBJECT );
// initialize preloader
preloader.initialize( );
} );
};
With a site load large enough to require an image preloader, the modal text display could be easily modified to support a data-driven jQuery animation.