Automated image preloading for a snappy UI

Posted by Scott on 03/06/2008

Topics:

NOTE: We made major improvements to this script and recommend that you read the updated post: Update: A New & Improved jQuery Script to Automatically Preload images from CSS. The original logic and functionality is outlined below.

We developed a solution that automates the age-old task of preloading images in web applications. It uses javascript and is packaged as a jQuery plugin, but the concept could easily be ported to other libraries or plain old Javascript as well.

Why is preloading important?

Web applications tend to involve a multitude of background images for elements and states that aren't in view when the page loads. In traditional web apps these images are used for rollover states for links and buttons, but Ajax applications have expanded the list to include images for inline messaging, tooltips, toggled content, overlay panels, and even entire pages of content that could potentially wind up in an interface. In order to create a snappy desktop-like experience on the web, images must be preloaded so that there is no blinking or fragmented loading. We found that our current options for preloading images were both insufficient and time-consuming, and it was clear that we needed to find a new approach.

How preloading is usually done

Developers tend to employ a couple of common practices:

  • CSS Sprites: Combine all states of an image (default, rollover, active) into a single image file and use CSS to position and crop it into place. While this solves the problem of preloading image states that aren't visible when the page first loads, it doesn't address how to preload background images that appear when new content is loaded via Ajax.
  • Using JavaScript to create image objects from filenames: Create an array of background-image filenames that need preloading, then loop through that array to create an image object for each image file. This ensures that images are cached and ready for later use, but often the number of images required in a complex Ajax application can be up to 50, 100, or more. On top of that, inevitable design changes throughout a development process mean constantly updating and adding file paths, which would lead most developers to simply not do it.

A typical array of images for preloading with JavaScript:

var imagesToLoad = 
  [
     'image1.gif', 
     'image2.gif', 
     'image3.gif', 
     'etc...'
  ];

How is this script different?

Our script parses through linked and imported stylesheets and creates an array of all the image urls they contain. Then it loops through the array of urls and creates an image object for each one so they'll be cached and ready for later use. Sound familiar? That's because it's nearly the same as the traditional JavaScript approach, but with the major added advantage of automation. Using this script means we only have to worry about specifying background images and states in our CSS files, and the script takes care of the rest.

Take it straight from the source:

/*
 * --------------------------------------------------------------------
 * jQuery-Plugin "preloadCssImages"
 * by Scott Jehl, scott@filamentgroup.com
 * http://www.filamentgroup.com
 * reference article: http://www.filamentgroup.com/lab/automated_image_preloading/
 * demo page: http://www.filamentgroup.com/examples/preloadImages/
 * 
 * Copyright (c) 2008 Filament Group, Inc
 * Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php)
 *
 * Version: 1.0, 31.05.2007
 * Changelog:
 * 	02.20.2008 initial Version 1.0
 * --------------------------------------------------------------------
 */
$.preloadCssImages = function(settings){
	//overrideable defaults
	settings = jQuery.extend({
		 imgDir: 'images'
	}, settings);

	//dump all the css rules into one string
	var sheets = document.styleSheets;
	var cssPile = '';
	for(var i = 0; i<sheets.length; i++){
		var thisSheetRules = document.styleSheets[ i ].cssRules;
		for(var j = 0; j<thisSheetRules.length; j++){
			cssPile+= thisSheetRules[ j ].cssText;
		}
	}
	//parse string for image urls and load them into the DOM
	var allImgs = [];//new array for all the image urls  
	var imgUrls = cssPile.match(/[^\/]+\.(gif|jpg|jpeg|png)/g);//reg ex to get a string of between a "/" and a ".filename"
	if(imgUrls != null && imgUrls.length>0 && imgUrls != ''){//loop array
		var arr = jQuery.makeArray(imgUrls);//create array from regex obj	 
		$(arr).each(function(k){
			allImgs[ k ] = new Image(); //new img obj
			allImgs[ k ].src = settings.imgDir +'/'+ this;	
		});
	}
	return allImgs;
}

How do I use it?

Paste the code above into your JavaScript file and run $.preloadCssImages() when the DOM is ready. Don't forget that you'll also need to reference the jQuery javascript library in your page. By default, the script assumes a root directory called 'images'. This default directory can be overridden by passing a different imgDir value to the settings object like so: $.preloadCssImages({ imgDir: 'myImagesDirectory' });

Time for a demo

The example below uses our script to parse through a sample stylesheet which we've linked to the page. The sample stylesheet has background image rules for elements that don't actually exist on the page, so the images specified are not currently loaded. Clicking the button below will load them into the DOM. For example purposes, we've written the loaded images into the page as well.

Demo page

Download preloadCssImages.jQuery.js

This script is a jQuery plugin, meaning is is dependant on the incredible jQuery javascript library. If you feel particularly adventurous, this script could be easily ported to another library or written in plain old JavaScript as well. Feel free to grab the script and try it for yourself. We're always looking for ways to improve our scripts, so if you encounter any issues or have any questions or suggestions please leave a comment below.

Future plans for preloadCssImages.jQuery.js

Currently this plugin assumes that all images are held in a single directory. Down the road, we would like to extend the script so that image paths are worked out in relation to the location of the CSS file, enabling images to be loaded from different directories on a server.

Check out the Belorussian translation of this article.

Book cover: Designing with Progressive Enhancement

Enjoy our blog? You'll love our book.

For info and ordering: Visit the book site

Comments

missing ; after for-loop condition
for(var i = 0l i0 && imgUrls != ‘’) { //loop array

I can’t seem to fix this by putting a ; after the condition…

Comment by Stuiter on 03/13  at  11:48 AM

Works perfect, if you put all your’s images into one folder (/images/).

Comment by doubled on 03/13  at  04:22 PM

@Stuiter: Thanks. It looks like there was a little character escaping issue in our sample code view. It should be fixed now but if you have problems, just grab it from source here:
/examples/preloadImages/scripts/preloadCssImages.jQuery.js

Comment by Scott (Filament) on 03/13  at  04:45 PM

@doubled:
Exactly. This is a limitation we’d like to get rid of in the future. There are some complexities we’ll have to work out with the way we parse for image URLs so that the entire url is mapped correctly. Since the script is likely to be located in a different directory than the stylesheets being parsed, any relative paths to images could get pretty tricky to re-path. We’ll need to consider the URL of each stylesheet as we parse it an work it out from there.

Sounds like a fun challenge though. Anyone up for it? Let us know!

Comment by Scott (Filament) on 03/13  at  04:56 PM

I’m new to JavaScript and trying to get this script to work. I have my page (index.php) calling up another php file (header.php) which contains the link to the javascript (js/preload...) and my CSS is contained within another folder (css/style.css). in this script where should my imgDir be directed to? ‘images’? or ‘../images’? Also, how do I run the $.preloadCssImages() when the DOM is ready? Thanks very much for any help. Page I’m trying to use it at is http://www.30aguide.com/temp

Comment by Spencer on 03/27  at  01:15 AM

@Spencer: The default directory that the script looks to is ‘images’, so it should work with your current directory structure without having to override the default. If you did need to override it with a different path though, the path should be specified in relation to the html document.

As for running scripts on DOM ready, jQuery (and most libraries) have a custom event you can use for that. It will let you run scripts as soon as the DOM elements are ready (before the images have finished loading), which is really handy for attaching behavior to the initial state of a page. The ‘ready’ event can be used with the following syntax:
$(document).ready( );

You can pass your DOM ready scripts as an argument like so:
$(document).ready(function(){ /*scripts go here*/ });
Or, in shorthand:
$(function(){ /*scripts go here*/ });

So in conclusion, you’d call the preloading script above like this:
$(function(){
$.preloadCssImages();
});

Comment by Scott (Filament) on 03/27  at  04:15 AM

@Spencer: Also, you can find a lot more info on the jQuery site. The documentation can be found here: http://docs.jquery.com/
If you have any more trouble with this script, feel free to email us so we can help you troubleshoot.

Comment by Scott (Filament) on 03/27  at  04:57 AM

Doesnt work - the images arent cached, the css reloads the images even though they are already cached.

Comment by durgle on 03/30  at  05:00 AM

@durgle: Sorry you’re having trouble getting it to work with your project. Perhaps the path to your images directory is different than the script’s default. In that case you’ll need to override that attribute as shown above.
Whatever the problem, we can assure you that it has worked for well others so if you’d like to shoot us an email we’d be glad to help troubleshoot!

Comment by Scott (Filament) on 03/30  at  03:35 PM

@Durgle: I checked out the page you sent [via email] and it seems to have gotten stuck at its loading screen so it was difficult to determine whether your images were preloading properly. Upon glancing at your source code though, it seems you may be using our script to do something slightly different than what we intend. I noticed you have an array of images referenced under var ‘imagesToPreload’. While you can certainly use javascript to loop through that array and preload the images, our script is actually meant to do all that work for you. We think you’ll find when you’re using this script properly it’ll make for a heck of a lot less work than what you’re doing now. Some quick pointers:

Since you’ve already linked to jQuery and our script, all you need to do is call our function on domReady, like so:
$(function(){
$.preloadCssImages();
});

It appears your images are held in an ‘images’ folder on the root, so that should be all of the scripting you need to do! By calling this function, you’ll tell our script to parse through all of your linked stylesheets and preload any images it encounters. This could be background images for hover states or even elements that aren’t currently on your page at all - anything referenced in the CSS will get preloaded.

It would be worth pointing out though that the script will ONLY load images referenced within CSS, so if you are trying to preload content images for a slideshow or something, you won’t need this script. Its purpose is strictly to make sure CSS background images are cached and ready when they’re needed in the UI.

One last note: if you’re planning to use the script on many levels of page directories, we’d suggest overriding the default images path with one that is relative to your public root. Here’s how that would look:
$.preloadCssImages({ imgDir: ‘/images’ });

Good luck getting that to work!

Comment by Scott (Filament) on 03/31  at  04:02 PM

@everyone: We wanted to post an update to you that a new version of the script has been posted. Among other improvements, images can now be loaded from any location.
You can view the updated article here:
http://www.filamentgroup.com/lab/update_automatically_preload_images_from_css_with_jquery/

Comment by Scott (Filament) on 06/05  at  03:50 AM

I’m getting an “Access denied” error when trying to read the cssText property in IE 7.0.

Comment by Rick on 08/18  at  08:06 PM

Thanks man but scripts does not work there is error

Comment by ahmedabad on 09/01  at  11:56 PM

>I’m getting an “Access denied” error when trying to read the >cssText property in IE 7.0.

If you load page via “http://localhost”, try load via “http://127.0.0.1”

On hosting all is ok.

It’s security issue.

Comment by perevaloff on 12/26  at  03:24 AM

The page is loaded via http://www.ryland.com, however the css file (and the images) are loaded via http://image.ryland.com.

Yes, I’m aware that it’s a security (cross-domain) issue.  Is there a way around this?

Comment by Rick on 12/29  at  04:55 PM

Try this.. “Cross Domain XMLHttpRequest using an IFrame Proxy”

http://manual.dojotoolkit.org/WikiHome/DojoDotBook/Book75

Comment by perevaloff on 12/29  at  07:23 PM

Does anyone know anything about the Mail/GrowlMail interface issues now WebKit has been updated? I finally removed GrowlMail, but Growl has not been updated in some time, and I don’t know how active the developers are. From all guesses a fair number of people use Growl, and I hope a fix is released soon!

Comment by craps hedge bets on 06/10  at  11:55 AM

he automatic installation of PHP Eclipse worked only once I changed the URL to a path which contained a file named ’site.xml’. For my installation this meant that the following URL needed to be entered:
http://www.alle-online-casinos.de

Comment by anthony on 06/29  at  02:03 PM

just read that Scott is on the jQuery design team, so you probably know about the conference.  :)

Comment by Taylor Kearns on 07/18  at  04:52 PM

Safari adds tabs to the top of the window (seemingly ‘aping’ Google’s Chrome, which isn’t out for Mac yet) and several new features for visual browsing (nevermind my own ‘top sites’; I haven’t used Safari for months). It sort of took me back for a second to the time where I mocked up the ‘dream browser’.

Comment by Birmingham Escorts on 08/19  at  05:53 AM

Thanks for the script codes!
It gets a error, but it was resolved removing the secure mode.
Nice tutorial.

Comment by Clinica de Estetica on 08/26  at  03:32 PM

thanks for this sick script
it’s a very good job guy

Comment by turfez on 10/08  at  09:59 PM

That’s always a good idea to use.

Comment by Valea Prahovei on 10/29  at  03:41 PM

I was looking for such information and while searching just found your post. Nice to read it and i hope it will also be successful for me to use.

Comment by stock and investment tips on 10/30  at  05:46 PM

It worked wonders. Thank you for this crafty script!

Comment by Birmingham Escorts on 11/06  at  03:33 PM

A simple pure css image preload method can be found here:

http://www.prismgraphicdesign.co.uk/blog/?p=12

Comment by Tony on 12/31  at  03:35 AM

Is the script free to use in commercial website which is used for image preload and stuff?

Comment by SB on 01/06  at  12:26 PM

Fantastic tutorial thank you!

Comment by Tim on 03/09  at  08:39 PM

Great work. Though would it be better to execute your preloader function on the window’s load event instead of the DOM ready event?

$(window).load(function() {
$.preloadCssImages();
});

I’m concerned calling the function on DOM ready could slow the loading of background images that are visible on initial page load. What do you think?

Comment by Alex Parish on 05/28  at  04:59 PM

Good work. Great tutorial. Thanks

Comment by Cartão de Visita on 06/18  at  08:16 PM

Very good work. Thanks for sharing.

Comment by Clinica de Estetica on 06/18  at  08:17 PM

Very useful advice! thanks

Comment by bijuterii argint on 09/30  at  01:13 PM

Thanks, everyone, for the feedback.  We’re no longer taking comments on this post.

An updated version of this plugin is here: Update: A New & Improved jQuery Script to Automatically Preload images from CSS

Comment by Maggie (Filament) on 06/23  at  07:42 PM

Book cover: Designing with Progressive Enhancement

Enjoy our blog? You'll love our book.

For info and ordering: Visit the book site