Skip to content


jQuery image Preloader Plus Callbacks

Hi all, After comments on the previous version by Roberto, I looked into creating callbacks within the code so that it can be used in the way that Roberto had outlined, also I used it as a way to improve my knowledge a little.

After a while I got stuck and figured out a potential method thanks to ai-a in #Javascript on UnderNet, then I spoke to Remy Sharp on Twitter and always being impressed with the work he does on his site (http://www.jqueryfordesigners.com) I asked politely if he would give my code a quick look over. He did so, and so much more besides.

So I present to you now v0.95 of the Image Pre-loader which is now Split into 3 functions (rather than the 2 from before)

So the main functionality is the same as the one from http://binarykitten.jkrswebsolutions.co.uk/2009/01/06/jquery-image-preloader/ but now has the following functions

  1. $.preLoadImages – Preload the passed list of images, calling the passed call back function when all images are preloaded
  2. $.preLoadCSSImages – Preload all images found within the stylesheets of the document, then call the passed call back function when all the images are preloaded
  3. $.preLoadAllImages – Processes the stylesheet images and then if passed additional images, will process them too. When all are complete will call the passed callback function.

Here’s the actual code now for the plugin:

/* jQuery.preloader - v0.95 - K Reeve aka BinaryKitten
*
* v0.95
* 	# Note - keeping below v1 as really not sure that I consider it public usable.
* 	# But it saying that it does the job it was intended to do.
* 	Added Completion of loading callback.
* 	Main Reworking With Thanks to Remy Sharp of jQuery for Designers
*
*
* v0.9
* 	Fixed .toString being .toSteing
*
* v0.8
*		Fixed sheet.href being null error (was causing issues in FF3RC1)
*
* v0.7
*		Remade the preLoadImages from jQuery to DOM
*
* v0.6
* 		Fixed IE6 Compatability!
*		Moved from jQuery to DOM
*
* v0.5
* 		Shifted the additionalimages loader in the preLoadAllImages so it wasn't called multiple times
* 		Created secondary .preLoadImages to handle additionalimages and so it can be called on itself
*/

(function ($) {
	$.preLoadImages = function(imageList,callback) {
		var pic = [], i, total, loaded = 0;
		if (typeof imageList != 'undefined') {
			if ($.isArray(imageList)) {
				total = imageList.length; // used later
					for (i=0; i < total; i++) {
						pic[i] = new Image();
						pic[i].onload = function() {
							loaded++; // should never hit a race condition due to JS's non-threaded nature
							if (loaded == total) {
								if ($.isFunction(callback)) {
									callback();
								}
							}
						};
						pic[i].src = imageList[i];
					}
			}
			else {
				pic[0] = new Image();
				pic[0].onload = function() {
					if ($.isFunction(callback)) {
						callback();
					}
				}
				pic[0].src = imageList;
			}
		}
		pic = undefined;
	};

	$.preLoadCSSImages = function(callback) {
		var pic = [], i, imageList = [], loaded = 0, total, regex = new RegExp("url\((.*)\)",'i'),spl;
		var cssSheets = document.styleSheets, path,myRules,Rule,match,txt,img,sheetIdx,ruleIdx;
		for (sheetIdx=0;sheetIdx < cssSheets.length;sheetIdx++){
			var sheet = cssSheets[sheetIdx];
			if (typeof sheet.href == 'string' &amp;&amp; sheet.href.length > 0) {
				spl = sheet.href.split('/');spl.pop();path = spl.join('/')+'/';
			}
			else {
				path = './';
			}
			myRules = sheet.cssRules ? sheet.cssRules : sheet.rules;
			for (ruleIdx=0;ruleIdx < myRules.length;ruleIdx++) {
				Rule = myRules[ruleIdx];
				txt = Rule.cssText ? Rule.cssText : Rule.style.cssText;
				match = regex.exec(txt);
				if (match != null) {
					img = match[1].substring(1,match[1].indexOf(')',1));
					if (img.substring(0,4) == 'http') {
						imageList[imageList.length] = img;
					}
					else if ( match[1].substring(1,2) == '/') {
						var p2 = path.split('/');p2.pop();p2.pop();p2x = p2.join("/");
						imageList[imageList.length] = p2x+img;
					}
					else {
						imageList[imageList.length] = path+img;
					}
				}
			};
		};

		total = imageList.length; // used later
		for (i=0; i < total; i++) {
			pic[i] = new Image();
			pic[i].onload = function() {
				loaded++; // should never hit a race condition due to JS's non-threaded nature
				if (loaded == total) {
					if ($.isFunction(callback)) {
						callback();
					}
				}
			};
			pic[i].src = imageList[i];
		}

	};
	$.preLoadAllImages = function(imageList,callback) {
		if (typeof imageList != 'undefined') {
			if ($.isFunction(imageList)) {
				callback = imageList;
			}
			else if (!$.isArray(imageList)) {
				imageList = [imageList];
			}
		}
		$.preLoadCSSImages(function(){
			if (imageList.length > 0) {
				$.preLoadImages(imageList,function(){
					callback();
				});
			}
			else {
				callback();
			}
		});
	}
})(jQuery);

So now with these in place we can call them as so:

     $.preLoadImages(
          [
               'http://www.google.co.uk/intl/en_uk/images/logo.gif',
               'http://l.yimg.com/eur.yimg.com/i/uk/hp/yahoo1.png',
               'http://tk2.stc.s-msn.com/br/hp/11/en-us/css/i/msn_b2.gif'
          ],function(){
               alert('All Passed Images Loaded');
          }
     );
     $.preLoadCSSImages(function(){
          alert('All CSS Images Loaded');
     });
     $.preLoadAllImages(
          [
               'http://www.google.co.uk/intl/en_uk/images/logo.gif',
               'http://l.yimg.com/eur.yimg.com/i/uk/hp/yahoo1.png',
               'http://tk2.stc.s-msn.com/br/hp/11/en-us/css/i/msn_b2.gif'
          ],function(){
               alert('All Passed Images and All CSS Images Loaded');
          }
     );

To match up with Robert’s request for a loader image…

$.preLoadImages('/images/loader.gif',function() {
  /* Pre Load the loader gif first */
  $('<img />').attr({
  	src:'/images/loader.gif',
  	id:'loader'
  }).appendTo('#position');
  /* now preload stuff */
  $.preLoadCSSImages(function() {
     $('#loader').remove();
  })
});

Hopefully you have found this useful, as usual.. comments gratefully received.

[edit]
Thanks to Roberto for pointing out that the source had become corrupted in the post

Posted in jQuery Plugins.

Tagged with , , , , , .


24 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. Roberto says

    That’s great, Kat! Thank you so much for giving attention to my requests. I’ll give it a try and post some feedback.

  2. Hasan H. says

    Great work! So easy to use..

  3. BinaryKitten says

    Thanks :)

  4. Rantie says

    will check this out… it will be great if it will work in ie678 opera 8+ safari 2+ chrome 3.0 and ff 2+

  5. Asa says

    Very nice, any chance you will compile using google closure.

  6. BinaryKitten says

    not looked at google closure yet for compiling.. but will do.

  7. BinaryKitten says

    Should work in all browsers.. if it doesn’t please do let me know

  8. troubled says

    I keep getting this error while trying to preload CSS images. Kind of a noob… Is there something I’m supposed to customize within the plugin?

    Error: missing ) after condition
    Column: 41
    Source Code:
    if (typeof sheet.href == ‘string’ && sheet.href.length > 0) {

  9. troubled says

    those ampersands and that greater than symbol were supposed to stay in html. arg

  10. fanton says

    There is a problem concerning css preload, firebug spits out the following url:
    http://somehost.com/css/%22../images/some_image.png%22
    which should look like this:
    http://somehost.com/css/images/some_image.png

    are we supposed to use absolute paths in our css stylesheets?

  11. fanton says

    got it, just append a substring to get rid of the quotation marks..

  12. BinaryKitten says

    thanks for pointing this out, Will update for the next version.. (coming soon)

  13. fanton says

    excellent script, but there’s another thing. i get a 403 error while trying to get attached css documents when testing it online.. no problem locally though..

  14. joey says

    Hey, when using @font-face and loading a font file in the attached CSS-Stylesheet, your script never fires a callback, since this if (loaded == total) is never reached, not sure why, but I dig into it.

  15. Ambrosia says

    Hey franton, what code did you add to fix your css image path problem? I’m getting the same thing.

  16. Saul Santiago says

    has problems with ie6 and 7. it seems to have problems with the loaded. i don’t know what specifically.

  17. BinaryKitten says

    It’s because it’s trying to load the font-face url into an image. Will need to add extra validation for those specific cases where the url keyword is not used for images and filter them out. Will think how to best achieve this for the next vesion. Due soon I hope.

  18. Kryptic says

    @Ambrosia
    Put following code in Line 77
    img = match[1].substring(2,(match[1].indexOf(‘)’,1)-1));

  19. Riball says

    In Firefox the preLoadAllImages function works… but when I open the same page in chrome it simply doesn’t.

    Stil looking what the prob is.

  20. Marcusklaas says

    Seems great. I will test this ;-)

  21. Stefan Bergfeldt says

    This is what Firebug tells me, and also the IE8 javascript error alert.

    Security error” code: “1000
    [Break on this error] myRules = sheet.cssRules ? sheet.cssRules : sheet.rules;
    preloadimages.js (rad 50)

  22. JohnnyP says

    Would processing be any quicker if I did not need to preload stylesheet images?

  23. Gareth says

    @Riball I’m having the same issue. I don’t suppose you ever found a fix?

  24. siconet says

    I doen’t get the Alert from the callback of preLoadAllImages (others are untested at the momen).

    @Kryptic: thx, your solution worked!

    thx for this good script!



Some HTML is OK

or, reply to this post via trackback.


Get Adobe Flash playerPlugin by wpburn.com wordpress themes