Even better image preloading with CSS2

Recently I read an article on better image preloading using CSS3 which presented a clever idea using CSS3 multiple background images to preload images on one element as opposed to another method of having containers for each image. As of writing the support for multiple backgrounds is fairly sparse with webkit having the best support (Safari 3+ & Chrome 1+), Firefox is introducing this in the upcoming 3.6 release.

There’s a better way

The article instantly got me thinking on a potential alternate method to accomplish the same task, but using a widely available CSS2.1 property. Which if possible will up the browser support significantly. Thankfully the idea actually worked.

CSS generated content

In CSS2 a content property was introduced, which in conjunction with the :before and :after pseudo-elements allows us to generate content in a document. The most interesting value that can be used with the content property is url() which allows us to pass an image.

content: url(img01.jpg);

That’s all great than we can add a single preloaded image using the content property but what about multiple images? That’s where the content property shows its true power, we can pass multiple url’s.

content: url(img01.jpg) url(img02.jpg) url(img02.jpg)

Brilliant, we can now preload multiple images using one line of CSS. We have all the benefits of the CSS3 method without the CSS3 and the Browser support grows exponentially.

  • IE8+
  • FF2+
  • Safari 3+
  • Chrome 1+
  • Opera 9.62+

Putting it all together

Now we know how to preload multiple images using CSS2, lets put it all together:

content: url(img01.jpg) url(img02.jpg) url(img03.jpg);
display: none;

Instead of creating an empty div with a class on it, we can utilise the requirement of the :after (or :before) pseudo-element. This needs to be there for the content property to work. We apply it to generate the content after the body tag and then set it to display: none, that way the preloaded images aren’t shown but are still loaded. We know have a pure CSS solution that requires no mark-up.

Almost perfect

IE6 and 7 do not support CSS generated content and therefore will not preload any images, but that’s a minor trade off. Users of IE6/7 will just have to load the images when they are called rather than getting them from the cache.

A possible work around for their lack of support is to use conditional comments to load the images. This solution will require extra mark-up.

Post filed under: css.

Skip to comment form.

  1. Rod Homor says:

    Nice one, Ninja. Good solution.

  2. Fantastic, clever trick. 🙂

  3. Ignazio says:

    Very nice idea. I immediately used it on my site. I gave you the credits in the code. 🙂

  4. Michael says:

    So if we use this should we preload every major image on the site?

    Also, would you place

    content: url(img01.jpg) url(img02.jpg) url(img03.jpg);
    display: none;
  5. Michael says:

    When I cache a lot of images, ff 3.6 seems to throw them all out as it it is caching nothing. I’m not hitting the browser cache limit because I have viewed the whole site then go back. It is clear it is caching.

    Is there a limit to the number of images or MB limit to the “content” that is set in the css.

    Note: I found out that you need to place the “body:after” section at the bottom of your css document for best performance.

    • One thing to make sure of is that you use the same path in the content property as you do when the actual image is called. If one is relative and other absolute the browser can potentially see this as a new image and not grab it from the cache.

      I’m not aware of any limit though I doubt it would have a limit on how much it can call.

  6. Michael says:

    Ok, my mistake. I was caching around 50 images. Apparently if you make one mistake in spelling of an image if will cause the cache to fail.



  7. kn33ch41 says:

    Very nice approach; it’s clean and degrades well.

  8. Catalin Red says:

    Very interesting, thanks for sharing!

  9. spooky says:

    Definitely the best way to preload images. Once I have found this great article, I have tested it and it worked better than any js preload technique. Now, I am using this method for every web site. Doesn’t work in IE6/7 …? 😀 What a pity…. 😛 (irony)… Who gives a damn about those crippled browsers anymore?
    Thnx ninja. Remarkable mind you have. Live long and prosper.

  10. Sarah says:

    This works perfectly for FF but I can’t seem to get the same effect on IE8, has anyone else run into this problem?

  11. Pol says:

    Thanks for the snippet works like a charm on every project.

  12. pako69 says:

    Like Sarah, i can’t get this to work with IE8 …

  13. kn33ch41 says:

    I’m revisiting this again. I wonder if it’s a bug that the browser will still download the images within content() if the :before/:after pseudo-classes are set to “display: none.” If the images were set as background images and the element set to “display: none” they would not download.

    • @kn33ch41 – I think you’re on to something here. I did some tests (Win box only) with background images and content referenced images with the :after pseudo-element set to display: none.

      Firefox 3.6: Will load all content referenced images but any background images won’t load.
      Safari 5.0.1 & Chrome 10: These two will both load background and content referenced images.
      Opera 10.62 & IE8: Won’t load any images.

      So it’s a mess. It’s hard to say who has the correct behaviour but we can get around it by not using the display property. All browsers will correctly download all content and background referenced images if we set the :after pseudo-element to visibility: hidden. So something like this revised CSS.

      body:after {
          content: url(img01.jpg) url(img02.jpg) url(img03.jpg);
          background-image: url(img04.jpg);
          visibility: hidden;
          position: absolute;
          left: -999em;
  14. kn33ch41 says:

    *pseudo-elements, augh.

  15. kn33ch41 says:


    My results with Safari 5.0.3 and Chrome 10.0 don’t follow with yours regarding the download of background images and the display property set to none.

    For several months I’ve defined certain decorative images (user profile pictures) on a site as background images instead of using the HTML img tag. I do this because when the viewport is small and assumed to be a mobile device they are set to “display: none” to avoid the download and request overhead. It works as intended in all browsers. The reason I delegated certain images to CSS to be rendered as background images is because setting “display: none” on HTML img tags does not prevent the resource from being downloaded.

    • @kn33ch41 – I’m still getting background images downloading even when the element is hidden in Safari and Chrome. Are you using media queries to detect viewport size?

  16. kn33ch41 says:


    After an hour of troubleshooting I discovered a quirk in Webkit regarding this difference in our results. The below solution works in both Chrome 10 and Safari 5.0.3.

    On Firefox and Opera if you define a background image on an element and set “display:none” to that very element, the image will not download. However, if you do this with Webkit, the image will still download. In order to prevent the image from being downloaded in Webkit, Webkit requires that one of the *parent* elements of the element with the background image be set to “display:none.”

    I hadn’t realized that this was occurring on my site by default, because I was never explicitly setting “display:none” on those decorative image elements, but always on one of their parent elements.

    It would appear, then, that the best way to preserve the most reliable, cross-browser behavior would be to set “display:none” on one of the parent elements of any given element with a defined background image. I’d be curious to see if you can duplicate these results; I’m confident you’ll be able to. It’s a handy tidbit of knowledge to have actually framed and defined, instead of being unaware of this subtle difference between browsers.


    IE is the only browser that does not suppress the download request, given the described usage.

  17. rebecca says:

    Thank you so much for this. I do a lot of sites for artists so large images. This technique has really made a difference to how they look. Very much appreciated.

  18. DrCord says:

    Great preloader! Easy to implement and works like a charm…thanks!

  19. Zenta says:

    Works like a charm. Elegant solution. Thank you for posting this.

  20. Donny says:

    Using this on my portfolio site (crediting CSS Ninja in the CSS file, of course) and it’s great. Only bit of oddness is that when I nav away from the home page, then back again, the “Loading” image/text reappear briefly.

  21. Bas says:

    Thanks this is also very useful for websites with changing background images.

  22. Sharon says:

    I find that if I use visibility:hidden with your body:after CSS that I get a load of blank space at the end of my page because it is reserving space for the images.

    “display:none” doesn’t cause this problem.

  23. anurag says:

    Hi This is a very good solution, but it does not works in IE. Any solution for that??