3 Ways to Preload Images with CSS, JavaScript, or Ajax

Post #733 categorized as Function, Optimization, last updated on Jan 9, 2010
Tagged with ajax, css, images, javascript, optimize, performance, preload-images, tips, tricks

Preloading images is a great way to improve the user experience. When images are preloaded in the browser, the visitor can surf around your site and enjoy extremely faster loading times. This is especially beneficial for photo galleries and other image-heavy sites where you want to deliver the goods as quickly and seamlessly as possible. Preloading images definitely helps users without broadband enjoy a better experience when viewing your content. In this article, we’ll explore three different preloading techniques to enhance the performance and usability of your site.

Method 1: Preloading with CSS and JavaScript

There are many ways to preload images, including methods that rely on CSS, JavaScript, and various combinations thereof. As one of my favorite topics here at Perishable Press, I have covered image preloading numerous times:

Each of these techniques sort of builds on previous methods and remains quite effective and suitable for a variety of design scenarios. Thankfully, readers always seem to chime in on these posts with suggestions and improvements. Recently, Ian Dunn posted an article that improves upon my Better Image Preloading without JavaScript method.

With that method, images are easily and effectively preloaded using the following CSS:

#preload-01 { background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px; }
#preload-02 { background: url(http://domain.tld/image-02.png) no-repeat -9999px -9999px; }
#preload-03 { background: url(http://domain.tld/image-03.png) no-repeat -9999px -9999px; }

By strategically applying preload IDs to existing (X)HTML elements, we can use CSSbackground property to preload select images off-screen in the background. Then, as long as the paths to these images remains the same when they are referred to elsewhere in the web page, the browser will use the preloaded/cached images when rendering the page. Simple, effective, and no JavaScript required.

As effective as this method is, however, there is room for improvement. As Ian points out, images that are preloaded using this method will be loaded along with the other page contents, thereby increasing overall loading time for the page. To resolve this issue, we can use a little bit of JavaScript to delay the preloading until after the page has finished loading. This is easily accomplished by applying the CSS background properties using Simon Willison’s addLoadEvent() script:

// better image preloading @ http://perishablepress.com/press/2009/12/28/3-ways-preload-images-css-javascript-ajax/
function preloader() {
	if (document.getElementById) {
		document.getElementById("preload-01").style.background = "url(http://domain.tld/image-01.png) no-repeat -9999px -9999px";
		document.getElementById("preload-02").style.background = "url(http://domain.tld/image-02.png) no-repeat -9999px -9999px";
		document.getElementById("preload-03").style.background = "url(http://domain.tld/image-03.png) no-repeat -9999px -9999px";
	}
}
function addLoadEvent(func) {
	var oldonload = window.onload;
	if (typeof window.onload != 'function') {
		window.onload = func;
	} else {
		window.onload = function() {
			if (oldonload) {
				oldonload();
			}
			func();
		}
	}
}
addLoadEvent(preloader);

In the first part of this script, we are setting up the actual preloading by targeting specific preload elements with background styles that call the various images. Thus, to use this method, you will need to replace the “preload-01”, “preload-02”, “preload-03”, etc., with the IDs that you will be targeting in your markup. Also, for each of the background properties, you will need to replace the “image-01.png”, “image-02.png”, “image-03.png”, etc., with the path and name of your image files. No other editing is required for this technique to work.

Then, in the second part of the script, we are using the addLoadEvent() function to delay execution of our preloader() function until after the page has loaded.

SO what happens when JavaScript is not available on the user’s browser? Quite simply, the images will not be preloaded and will load as normal when called in the web page. This is exactly the sort of unobtrusive, gracefully degrading JavaScript that we really like :)

Method 2: Preloading with JavaScript Only

As effective as the previous method happens to be, I generally find it to be too tedious and time-consuming to actually implement. Instead, I generally prefer to preload images using a straight-up slice of JavaScript. Here are a couple of JavaScript-only preloading methods that work beautifully in virtually every modern browser..

JavaScript Method #1

Unobtrusive, gracefully degrading, and easy to implement, simply edit/add the image paths/names as needed — no other editing required:

<div class="hidden">
	<script type="text/javascript">
		<!--//--><![CDATA[//><!--
			var images = new Array()
			function preload() {
				for (i = 0; i < preload.arguments.length; i++) {
					images[i] = new Image()
					images[i].src = preload.arguments[i]
				}
			}
			preload(
				"http://domain.tld/gallery/image-001.jpg",
				"http://domain.tld/gallery/image-002.jpg",
				"http://domain.tld/gallery/image-003.jpg"
			)
		//--><!]]>
	</script>
</div>

This method is especially convenient for preloading large numbers of images. On one of my gallery sites, I use this technique to preload almost 50 images. By including this script on the login page as well as internal money pages, most of the gallery images are preloaded by the time the user enters their login credentials. Nice.

JavaScript Method #2

Here’s another similar method that uses unobtrusive JavaScript to preload any number of images. Simply include the following script into any of your web pages and edit according to the proceeding instructions:

<div class="hidden">
	<script type="text/javascript">
		<!--//--><![CDATA[//><!--

			if (document.images) {
				img1 = new Image();
				img2 = new Image();
				img3 = new Image();

				img1.src = "http://domain.tld/path/to/image-001.gif";
				img2.src = "http://domain.tld/path/to/image-002.gif";
				img3.src = "http://domain.tld/path/to/image-003.gif";
			}

		//--><!]]>
	</script>
</div>

As you can see, each preloaded image requires a variable definition, “img1 = new Image();”, as well as a source declaration, “img3.src = "../path/to/image-003.gif";”. By replicating the pattern, you can preload as many images as necessary. Hopefully this is clear — if not, please leave a comment and someone will try to help you out.

We can even improve this method a bit by delaying preloading until after the page loads. To do this, we simply wrap the script in a function and use addLoadEvent() to make it work:

function preloader() {
	if (document.images) {
		var img1 = new Image();
		var img2 = new Image();
		var img3 = new Image();

		img1.src = "http://domain.tld/path/to/image-001.gif";
		img2.src = "http://domain.tld/path/to/image-002.gif";
		img3.src = "http://domain.tld/path/to/image-003.gif";
	}
}
function addLoadEvent(func) {
	var oldonload = window.onload;
	if (typeof window.onload != 'function') {
		window.onload = func;
	} else {
		window.onload = function() {
			if (oldonload) {
				oldonload();
			}
			func();
		}
	}
}
addLoadEvent(preloader);

Ahhh, the joys of JavaScript!

Method 3: Preloading with Ajax

As if all of that weren’t cool enough, here is a way to preload images using Ajax. This method was discovered at Of Geeks and letters, and uses the DOM to preload not only images, but CSS, JavaScript, and just about anything else. The main benefit of using Ajax over straight JavaScript is that JavaScript and CSS files can be preloaded without their contents affecting the current page. For images this is not really an issue, but the method is clean and effective nonetheless.

window.onload = function() {
	setTimeout(function() {
		// XHR to request a JS and a CSS
		var xhr = new XMLHttpRequest();
		xhr.open('GET', 'http://domain.tld/preload.js');
		xhr.send('');
		xhr = new XMLHttpRequest();
		xhr.open('GET', 'http://domain.tld/preload.css');
		xhr.send('');
		// preload image
		new Image().src = "http://domain.tld/preload.png";
	}, 1000);
};

As is, this code will preload three files: “preload.js”, “preload.css”, and “preload.png”. A timeout of 1000ms is also set to prevent the script from hanging and causing issues with normal page functionality.

To wrap things up, let’s look at how this preloading session would look written in plain JavaScript:

window.onload = function() {

	setTimeout(function() {

		// reference to <head>
		var head = document.getElementsByTagName('head')[0];

		// a new CSS
		var css = document.createElement('link');
		css.type = "text/css";
		css.rel  = "stylesheet";
		css.href = "http://domain.tld/preload.css";

		// a new JS
		var js  = document.createElement("script");
		js.type = "text/javascript";
		js.src  = "http://domain.tld/preload.js";

		// preload JS and CSS
		head.appendChild(css);
		head.appendChild(js);

		// preload image
		new Image().src = "http://domain.tld/preload.png";

	}, 1000);

};

Here we are preloading our three files upon page load by creating three elements via the DOM. As mentioned in the original article, this method is inferior to the Ajax method in cases where the preloaded file contents should not be applied to the loading page.

Know some triks?

I love these preloading tricks so much, I could just squeeze something. If you know of any good preloading tricks, including any improvements to the techniques shared here, kick start my heart with your wise words of preloading wisdom ;)

Subscribe to Perishable Press


50 Responses

TopLeave a comment

[ Gravatar Icon ]

#1iPad

Great tips Jeff, thanks a lot. For you what’s the best method? :)

[ Gravatar Icon ]

#2Jeff Starr

I tend to prefer JavaScript Method #1, mostly because of its easy implementation.

[ Gravatar Icon ]

#3Montana Flynn

Great article Jeff, I will be using this for my new projects. Thanks. PS. Can you write an article about caching images?

[ Gravatar Icon ]

#4JohnKraft

This is a very good post and i also like your blog page layout too. Bookmarked your site and will stop by again

[ Gravatar Icon ]

#5Miller Medeiros

another technique that’s even simpler is to add the images that you need to preload inside a ‘div’ with display ‘none’ inside the HTML… that way you don’t need JavaScript or any extra CSS to preload the images.

configure Etags and add an expires header to the images.. that way you will force a strong caching and will make the preload work better.

I’m not sure if it happen this way on all browsers but Firefox loads inline images before CSS background images. So using this technique can be useful if for instance you need to load the “css sprites” before the actual content images (I’m using this technique on my website..) - If you want to check the load order on any website just enable the “net panel” on Firebug to test it.

cheers.

[ Gravatar Icon ]

#6Jeff Starr

@Montana Flynn: Actually yes, I have something in mind for a caching article and will be working on it this coming month. Stay tuned..

@JohnKraft: Thanks for the feedback :)

@Miller Medeiros: Absolutely - that is another fine way to preload images. At the beginning of this article, I link to my initial post using the CSS-only method, and also the follow-up article, wherein I attempt to improve the original technique.

[ Gravatar Icon ]

#7Deano

I’ve used:

for preloading images/URLs for Firefox browsers for ages :D I think there are some other browsers that support it too.

A nice simple way (place code in the head of html doc).

[ Gravatar Icon ]

#8Jeff Starr

@Deano: It looks like WordPress ate your code - could you try again or perhaps send the code to me at “jeff” at this domain and I will post manually..? Thanks.

[ Gravatar Icon ]

#9Alex Flueras

The code is not eaten :). You just have to place your pointer over the code and it will expand… Great article, thanks for sharing.

[ Gravatar Icon ]

#10justin

Nice article!

Css sprites:
I saw a very cool sprite conversiom at this site: http://www.makebetterwebsites.com/

David uses one very small image for the entire page and in may eyes it works great.

What do you think about it?? :-)

[ Gravatar Icon ]

#11Jeff Starr

Hi justin, image sprites are a great way to improve performance by reducing the number of HTTP requests. I use them here at Perishable Press and on many other sites — they are an important tool and a good way to preload your CSS background images. The preloading method described in this article is actually geared more toward caching of bulk images such as used in galleries, portfolios, shopping carts, etc., but also works fine for preloading sprites.

[ Gravatar Icon ]

#12russell

Incase you cant read this again - your image sources on the second method are repeated rather than incrementing the image number…
copy and paste are a bitch.

img1.src = "http://domain.tld/path/to/image-001.gif";
img1.src = "http://domain.tld/path/to/image-002.gif";
img1.src = "http://domain.tld/path/to/image-003.gif";

you mean:

img1.src = "http://domain.tld/path/to/image-001.gif";
img2.src = "http://domain.tld/path/to/image-002.gif";
img3.src = "http://domain.tld/path/to/image-003.gif";

[ Gravatar Icon ]

#13Jeff Starr

Good catch, russell - fixed :)

[ Gravatar Icon ]

#14Tony

Useful post.

Another very simple way to pre-load images using CSS can be found here : http://www.prismgraphicdesign.co.uk/blog/?p=12

[ Gravatar Icon ]

#15justin

If i remember well, did i read an article which said that display:none isn’t a suitable solution because of validation problems or the else… but i am not sure.
I think that this article mentioned the most modern and recommended solutions for image preload. :-)

[ Gravatar Icon ]

#16Basch

Don’t use all weird and complex techniques to preload images! Use one image sprite instead! (Less requests, smaller filesize and faster rendering)

[ Gravatar Icon ]

#17justin

hehe, my words :P

[ Gravatar Icon ]

#18Jeff Starr

@Basch, @justin: as discussed in a previous comment, image sprites are useful for background images, but are not practical for preloading actual images (i.e., those called via <img> tags). And even for background images, excessively large sprites — such as would be necessary for visually complex designs — impact performance on the user’s browser because of the large amount of memory required to render/display them.

Bottom line: use sprites for small numbers of background images and keep their sizes as small as possible. For anything else, consider preloading and/or lazy loading.

[ Gravatar Icon ]

#19Michael Risser

Great post, lots of useful methods. I keep a number of commonly used JavaScript functions in a single file that I load into each page, rather than mucking about with the function for every site, I modified the first JavaScript + CSS function to dynamically append the divs to the page. Just pass an array of image paths to the function when you call it.

This makes it much more portable, and if for some reason you should want to preload a different set of images, just pass the function a different image array on those pages.

For the benefit of your readers, here’s the modified function:

// Preload images, takes an array of image paths as its only argument,
// loops throught the array and sets each image as the background of a div,
// the div is then appended to the body of the document.
function img_preloader(images) {
       if(document.getElementById) {
              for(img in images) {
                     var newdiv = document.createElement('div');
                     newdiv.style.background = "url(" + images[img] + ") no-repeat -9999px -9999px";
                     document.body.appendChild(newdiv);
              }
       }
}

[ Gravatar Icon ]

#20Jeff Starr

@Michael Risser: thanks for the feedback — your method sounds great, but unfortunately WordPress seems to have gobbled the code. If you want to send it via email to jeff at this domain, I would be happy to (re)post it for you, and for the benefit of others. Thanks :)

Edit: Thanks for sending the code! I have updated your comment to include it. Looks awesome :)

[ Gravatar Icon ]

#21Justin

@Jeff: hehe, absolutely correct. I really appreciate your listing of several methods to solve this problem. I just meant that sprites are the easiest solution, in my eyes, but of course: only if possible = smaller images. Complex and large designs with big images: not that useful and your methods would be much more interesting in such a case :-) so, thanks!

[ Gravatar Icon ]

#22Dan

Preloading images that are not currently displayed but will be onMouseOver or :hover is cool, but you might want to take into account a different scenario.

For instance, pre-loading images that are required BEFORE onLoad is called, as onLoad is when all page assets have been fully loaded (including images!). What if we wanted to load images / content before a page is fully rendered / revealed? In this case, it would be more appropriate to use something like MooTools’

window.addEvent( 'domready', .... );

or jQuery’s

$( function( ){ ... } );

here’s an example I did for my DJ page a while ago (djfunkthesystem.com):

window.addEvent( "domready", function( )
{
       var shower_object = $( 'shower' ),
              hider_object = $( 'hider' ),
              fx_hider = new Fx.Tween( hider_object, { duration: 3000 } ),
              fx_shower = new Fx.Tween( shower_object, { duration: 3000 } ),
              preloader = new Image;

       fx_shower.set( 'opacity', '0' );

       preloader.onload = function( )
       {
              shower_object.style.backgroundImage = 'url(' + this.src + ')';
              fx_hider.start( 'opacity', '0' );
              fx_shower.set( 'visibility', 'visible' );
              fx_shower.start( 'opacity', '1' );
       };

       preloader.src = 'funk_the_system_bg.jpg';
} );

Which waits until the Image has loaded, then assigns the CSS property to the . This might’ve been done in some of your examples, I didn’t really look past this page, so if this is the case I’m sorry.

It just seemed weird to make an onLoad example to pre-load something IF it’s on the page (because that means it’s already been loaded).

[ Gravatar Icon ]

#23Jeff Starr

@Dan: great point about using onLoad for images already included on the page. Users of either script in method #2 should be advised that they are effective for preloading images that are used on subsequent pages. For same-page uploading, use either of the other methods or take advantage of jQuery or MooTools as you suggest.

[ Gravatar Icon ]

#24Pascal

@Jeff: Very interesting article as usual. And I find this theme excellent.

Beside this, being very very new to javascript, could you tell me more about the variable definition required for method #2 using javascript? How it differ from the path to the source….? (A newbie question indeed!)

Thanks

[ Gravatar Icon ]

#25Jeff Starr

@Pascal: ah, that’s just because I copied and pasted each of these techniques from different projects that I was using them in. I changed the names of the path components, but forgot to synchronize them. For either method, use the paths that correspond to your actual images.

[ Gravatar Icon ]

#26Vijendra Mishra

Nice Tricks … it’s really cool. Thanks

[ Gravatar Icon ]

#27Perry

I’ve read a number of these Image Preloading techniques today and would like to know the best approach to use.

I use PHP 5.3 on the server that presents a large 900×900px PNG image that is repeated as the background on several other pages. I call this TemplateGUI.

Second, based upon user selection new images are layered overtop of TemplateGUI through PHP/MySQL 5.1 queries. This includes a 10-button menu panel and a 600×600px PNG map image. The final query layers several tiny dots overtop the map image.

……………………….
What I see happen on page loading is the final query renders first, followed by the 10-button menu panel, the TemplateGUI, and the last to render is the map image. I believe this happens because of file size.

I’d like to render in the following order; TemplateGUI, 10-button menu panel, map image, and the final query of tiny dots.

[ Gravatar Icon ]

#28smith

Thanks jeff for sharing this method.

[ Gravatar Icon ]

#29Šperky | Velko-Obchod.com

Hey It does work for me. I think it is really good.

[ Gravatar Icon ]

#30Brett Buchanan

Any help on how to incorporate the Better Method #1 into a wordpress page?
Thanks,
Brett

[ Gravatar Icon ]

#31Candace A

Jeff - your site is neat as shit. How do you find the time to learn all this ?

[ Gravatar Icon ]

#32Jeff Starr

Candace, Thanks - I’ve spent five years working on it :)

[ Gravatar Icon ]

#33Portik Norbert

Hi Jeff! :D
Thank you very much for those great ideas, and thanks for anyone else too, who helped Jeff. :)
I had to make a thumbnail imageviewer, so i used the js method #2. Works great. I didn’t read all the comments, but i have a suggestion if anyone has the problem, loading a lot of images: use 2 divs on the same position. The frist div should show a loading gif animation, the second should be the main (thumbnail, or image container). You can set a timeout and if that expires, you toggle the invisibility (first time loading the page 2nd div should be invisible, but still loading). This should be done to gain some more time for the preloader (if there is no login division). I hope, that i didn’t said noob thing, sorry if yes (im pretty newbie).

[ Gravatar Icon ]

#34Snap

Well, nothing new for images preloading, but the last trick to preload any HTTP resource with ajax is great ! Thanks :)

Share your thoughts..

TopRead official comment policy

The rules are simple. Comment intelligently. Stay on-topic. Don’t spam! Suspected spam will be deleted. Use your real name or nickname, not a site name or business name. Using a site name or business name is a good way to get your link or comment removed. Certain comments are moderated; if your comment does not appear after several days, or if you wish to comment privately, contact me. Also, by posting a comment, you grant this site a perpetual license to reproduce your comment, name, and website URL. Lastly, you may use basic HTML markup, but please do not use <pre> tags. Instead, wrap your code with <code> tags. Use a new set of <code> tags for each code term or phrase, as well as for each individual line of code (i.e., multiple lines of code require multiple code tags). Please see the complete comment policy for more information.