Skip Navigation

Preload Images with CSS

As support for CSS improves, pseudo-selectors like :hover, :active and :focus will become more widely used. Already :hover is in use on many sites to provide rollover states to buttons, as on this site (the menu bar). The other pseudo selectors will, in time, give far more opportunities for the use of rollover images.

One potential problem with image rollovers, though, is that in order for an image to be displayed, it must be downloaded. Consequently, for rollovers to work smoothly and quickly, all the necessary images must be already available on the user's PC. Otherwise, the rollovers will behave badly, like in this example using large images.

Until recently, rollover effects were achieved through use of JavaScript, and as a result, a plethora of solutions to the preloading image problem in JavaScript are available. However, using JavaScript to preload images, though not a bad idea when using JavaScript to control rollovers, becomes less bright when it is CSS that's controlling them. A user could very easily (and this is becoming more common) have a CSS-capable browser without JavaScript support or with JavaScript turned off.

So there's a clear need for a way to use CSS to preload images or find another way to avoid the problem. Which gives us two relatively simple solutions to our problem.

The first solution is to create a single background image for your element that actually contains both the rollover and non-rollover images, and then position is using the background-position CSS property. Instead of changing the image when the mouse moves over the element, you can simply change the background-position to reveal the previously hidden rollover image. There's a more detailed explanation of this technique over at WellStyled.com.

The other option available to you is to trick the browser into downloading the image before it is required for the rollover. This can be done by applying the image as a background to an element, and then hiding it using the background-position property. The image will then be downloaded but will not be displayed. Then, when the rollover is activated, it will operate smoothly and instantly.

First, you need to select an element that doesn't currently have a background image. If so select an element that does have a background image, you will either end up not preloading the image you are after, or you will prevent the element's normal background displaying. Neither is ideal.

Once you have picked an element to use for this purpose, you need to add the background image. The following CSS can be applied to the element and will place the background image outside the viewable area of the image:

  1. background-image: url("rollover_image.png");
  2. background-repeat: no-repeat;
  3. background-position: -1000px -1000px;

Your rollover image will then be loaded when the page itself is initially loaded, along with the other images. When a rollover is then activated, the image will already be available to the browser and the effect will be instant.

45 comments

Good idea ... but what would you do for several images? Assign each image as backgrounds for various page elements? This might get a bit cumbersome. Not had a chance to think about this but could you use display: none or something like it?
Using display:none; should mean the image is downloaded, but presents an accessibility issue.

If you were using a lot of images, this would potentially be unweildy. However, in most cases only one or two images would be needed.
A pretty clever approach. It looks like a good alternative to the Wellstyled approach. Thanks.
Zohar
France #4: May 6, 2005
But then comes IE...
IE doesn't see the :hover selector when it comes to anything else but <a> tags...

Any known solution for that?


And about using more than one background image in a page - you can make one image with several background images on it, and play with the position the same way.
I been using a DIV tag with a display:none as attribute, and there inside dumped the image-overs I needed, with quite crossbrowser success so far and making acorrect user experience...
Jamm!n
United Kingdom #6: May 27, 2005
Re the last comment - unfortunately some browsers do not load images that are inside a display:none block (or that have display:none on the img tag itself). I've hit this problem with Moz 1.2.1, apparently it also affects Opera and some versions of IE.

See http://www.quirksmode.org/css/displayimg.html
Jamm!n
United Kingdom #7: May 27, 2005
Is there any advantage in using background-image as suggested here, rather than just putting the images in a block which is then positioned off-screen?

div.hidden {
position: absolute;
top: -4000px;
}

Or does this have the same problem as display:none in some browsers? (Damn those optimisations...)
Just adding an empty div with a particular background-image should work. Try adding this div to the end of the page instead of the beginning, that way, hover preloads won't adversely affect page display time. Since it's zero-height being an empty div, it won't affect the layout no matter where you put it. Since it doesn't have display:none, the browsers don't have a cue to mindlessly neglect loading it.

/* Plain and simple. */
.preload
{
background-image:url(hover.png);
}
<body>
...
<div class=preload></div>
</body>
Anonymous
United States #9: November 11, 2005
load them using a iframe that is hidden by having a smaller z-index than a blank white div in front.
Anonymous
United Kingdom #10: November 22, 2005
Don't use iframes. Just use a div and assign the div the background-image which you want to see on hover. Then ensure your link sits within the div. Set the background-image for the a tag to the 'normal' state, and the hover state a:hover { display:none;}. This way the background for the a tag will be hidden on rollover, revealing the background loaded on the div enclosing it.
I agree the best way to do it is using div instead frames.
Joel Watson
United States #12: February 2, 2006
Here's a great way to do it.

#preload { height: 0; overflow: hidden; }

<div id="preload"><img src="..." /><img ... ></div>

It doesn't affect flow, it doesn't show, it works for Opera, Firefox, and IE on a PC that I've tested. The downside is that there is HTML markup. But this is far less hassle than working with Javascript and overcomes the CSS On/Javascript Off problem.
It may be that images aren't downloaded because - according to the spec - a box isn't generated when display: none; is set.

However, visibility: hidden; still creates a box and affects the flow of the document, so I would think that all browsers would download the background, regardless of the box's visibility.

Of course, this is just supposition. If anyone has tried this method - and can verify or dismiss this suggestion - please do.
Good idea, but if I want to preload more than 1 image?, anybody think that is better to use javascript?
RE:#14 -

If you want to preload multiple images, you could just create a class with the CSS to hide & preload the image, while keeping the image-specific CSS in another class/id.

Alternatively, you could probably create a wrapper div with the preload CSS, and then put the images within it. The images will load, and assuming they're relatively positioned, they should remain hidden offscreen along with the parent div.

(Or, if you opt to hide the parent div with visibility:hidden instead of positioning itabsolutely and offscreen, they should inherit that. Same effect, slightly different method.)

Sure, it creates extra code, but so would any other method. Regardless of how you do it with CSS, you won't have to worry about javascript being turned off on the client-side.
To Joel Watson - thanks for your idea. It have used it on my site and it seems the most simple solution. Cheers!
Dave sadler
United States #17: April 23, 2006
Is there any downside to:

#preloadDefaultImages {
width: 0px;
height: 0px;
display: inline;
background-image: url(~/App_Themes/Basic/images/fill_over.gif);
background-image: url(~/App_Themes/Basic/images/login_over.gif);
background-image: url(~/App_Themes/Basic/images/review_over.gif);
background-image: url(~/App_Themes/Basic/images/shred_over.gif);
}


<body>
<div id="preloadDefaultImages"></div>
I like that method Dave! Works for me. Thanks.
Great tips man! I *knew* there was a way to do this, but couldn't find the answers. Now I have! Thanks Dave!
pete
Canada #20: July 28, 2006
That seems to work well for opera, firefox, and ie, Dave. However, Netscape doesnt seem to honor the philosophy (at least in my first tests). It appears that netscape does some prliminary rendering of the html first and decides it doesnt need to request the preloaded images - i would guess because of its dimensions.
pete
Canada #21: July 28, 2006
So, I meant to say, I guess that brings us back to the author's original idea which works nicely in all 4 browsers.

BTW - does the author/webmaster of this page know that these addedbytes pages render with no content in Netscape (7.1) ?
you can simply hide the div containing the images...

.preloader {
position:absolute;
top:-1000px;
left:-1000px;
}

seemed to
http://www.addedbytes.com/css/preloading-images-with-css/comments/#comment12

<div class="preloader"><img src="blabla.jpg"></div>
Good tips people!

I have gone for Joel Watson's tip.

Top stuff!
This is infact good solution for Opera and Mozilla based browsers but not for IE. Internet Explorer dosent support image preloading with CSS.
visibility: hidden;
position: absolute;

Works perfectly for me, no dodgy hacks.
All what you proposed does not work with IE 6 properly, any idea?
r2b232
United States #27: November 26, 2006
I just found tried this and it worked for me:

#nav li a:hover{
background: url(/images/nav-item-up.jpg) no-repeat;
_cursor: hand;
_background: none; filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/images/nav-item-up.jpg',sizingMethod='crop');
color: #ffff00;
}

IE doesn't load the filter on every roll-over, no flicker, but I needed to add the cursor: hand to make it seem like a regular link.
I used #22's tip (LeoCavallini) and it works wonderful even with multiple images. I also checked it with the latest IE, Mozilla and Opera with no problems.

I still use javascript for my roll overs but #22s tip combines with it wonderfully. Thanks
I experience problems too with IE7 and FF2.
So I developed my own script which can be found here:
http://www.haan.net/test/preloader1.php

Please feel free to copy, paste and adjust to your needs.
Thanks for the trick. I hated that "small" time you must wait for the image to load.
I came up with the following solution for may site

.loadbg {
display:none;
height: 0px;
margin: 0px 0px 0px 0px;
padding: 0px 0px 0px 0px;}

<img src="images/hmleftbg.gif" class="loadbg">
gcyrillus
France #32: April 5, 2007
i came up with the same idea for preload in css , there is so many tags with no backgrounds that it is quiet easy to insert.
Very useful for roll overs in menu with hidden text .
img tags can as well hold css preload in their backgrounds :).

For the display:none; option , i might remenber that netscape will not preload the background , following strictly the rule display:none; it will not care about it at all.
beside the height:0; overflow:hidden; and z-index:-1 (for opera) works fine too to hide somethings , but it could become extra tags ... where using empty backgrounds makes it easier :)

great site !

GC
Thanks for sharing this idea, I was looking for a css preload technique for my image rollover menus. I appreciate the help!
This method works most of the time but when you are working with multiple layers or two different images in IE you will sometimes get flicker. If you load a wrapper with the same image (in my case I used a table td around a nested div identically sized) you can fool IE. Basically it is smoke and mirrors. In the gap between the time it takes the image to change you just show a repeat of the first image.

If you are real sharp I guess you can code in a browser detection so that Firefox doesn't pay for IE's slow transitions.

Thanks Dave! I have enjoyed your site again and again! Just like my JD!
#34 touched on the issue of IE flicker. In the first technique (the Pixy method: one image with both states, revealed by background position), IE can flicker b/c that browser pings the server for the image again (even though it shouldn't).

The same fix should work. Apply the same background image, in hover state position, to the element's parent. In that instant while the image flickers on hovering the element, the parent's copy of the image rollover will be there to save the day. This works best with simpler designs, of course.

For example:
<p><a href="#">hover me</a>

/* background image */
a, a:link, a:visited {background:url(image.gif) 0 0 no-repeat;}
a:hover {background-position:0 -50px;} /* image moved up whatever distance needed */

/* background image in rollover position, applied to parent element */
p {background:url(image.gif) 0 -50px no-repeat;}

Also, a few earlier comments noted another problem with that fact that IE doesn't apply the :hover pseudo-class to anything else but <a> tags. There is a behavior file out there that fixes this - search for "csshover.htc", problem solved!
 United Kingdom #36: April 18, 2008
thanks!! nice one
This is absolutely brilliant. I have been searching for a non-javascript solution to do this for ages. Cannot thank you enough. ILove[redacted].com!
joel watson #preload { height: 0; overflow: hidden; }

<div id="preload"><img src="layout1.png" /><img ... ></div> what to put inside <img ...>???
Here is what I do. If this helps anyone, terrific... I'll go buy myself a snickers bar and feel all fuzzy inside. If not... I'll sink into a deep depression and rock back and forth in the darkest corner of my office muttering "Mad Word" by Tears for Fears...

In my CSS:

#preloader {
position:absolute;
top:-1000px;
height:100px;
width:100px;
overflow:hidden;
}

Then at the very top of my page under the body tag:

<div id="preloader">
<img src="preload-image-1.gif" height="10" width="10" border="0" />
<img src="preload-image-2.gif" height="10" width="10" border="0" />
<img src="preload-image-3.gif" height="10" width="10" border="0" />
etc...
</div>

This will not be visible on your pages, and *should* take care of any browsers that do not render images if a "0" size is given to them or if they are "hidden".

I actually use this to eliminate the delay that browsers seem to have loading CSS background images but I can't see how it wouldn't work for rollover images as well.

I have tested this using FF and Safari on Mac as well as IE6 and IE7 on Windows. I have not tested it in the lesser used browsers because, truth be told, shooting myself in the face right this second with a 12 gauge shot gun would be a more joyous alternative to the self-torture I'd subject myself too if I tried to please the logic/rendering engines of every single browser. In the words of the late, great Bob Marley: "You can't fool all the people all the time..."

Cheers!
A co-worker of mine suggested using CSS sprites technique (http://alistapart.com/articles/sprites/), since the bg image is loaded when you load the pre-hover state of the element, all you would need to do is specify background positioning for the css hover declaration.
I have been using CSS display:none effectively (.hiddenPic {display:none;}, but I'm not testing with any older browsers, which is a worry.

I just tried #39 (Dameian) and it works fine for everything EXCEPT Chrome, which puts the little 10x10 icons of the images in the lower left corner, obviously not processing the -1000 positions.

I then added the .hiddenPic class to the img lines, and now even Chrome is handling it correctly, as expected since it is the new kid on the block.

Thanks, all. Great site.
Joseph
Rakesh
India #42: February 5, 2009
I am facing a totally opposite issue :) Is there a way to "prevent" loading of images until some action. I have a div which has 100+ images in it. I want to show the div only on some action by the user. Even though the div has display:none set, the images gets preloaded. Is there a way to solve this ?
well after reading all this I ended using an iframe for our company site http://www.teamquest-inc.com.ph , this is because I need to preload more than ten image and a swf/flash file that will be used in other page of my site, so in the home page i insert iframe in a div position off screen. this works for our company site.
 Los Angeles, CA #44: May 22, 2009
A technique that I use drastically reduces the number of HTTP requests required for image preloading in addition to eliminating the need for any additional code to preload images. I've written a brief post on it here: http://www.bjornenki.com/blog/preload-roller-images-without-css-javascript
 Pennsylvania, US #45: June 22, 2009
After reading all this and *most* of the comments, I tried just adding div wrappers around my list items, and adding the rollover background to them with an id tag.

My challenge was that I'm building a list of categories (for my portfolio) as a navigation for the portfolio categories. But I'm using my text in the images, so I need separate backgrounds for each category, being 9 total.

So what I did and seemed to work out well was to wrap each <li> in <div> tags, and give each a separate id, such as:
<div id="preload_print_ro">...then target each one with just a CSS rule for the background image.

Now, instant rollovers, no flicker!

Post Your Comment

· Comments with keywords instead of a name have their URLs removed.
· Your email address will not be displayed or shared.

Live Comment Preview

 United States #46: 1 minute ago