Save 10% on our Pro WordPress plugins with discount code: 10PERCENT
Web Dev + WordPress + Security

Pure CSS: Better Image Preloading without JavaScript

[ Preload Images with CSS ] After reading my previous article on preloading images without JavaScript1, Nanda pointed out that adding extra markup to preload images is not the best approach, especially where Web Standards are concerned. Mobile devices, for example, may experience problems when dealing with the following preloading technique:

div#preloaded-images {
   position: absolute;
   overflow: hidden;
   left: -9999px; 
   top: -9999px;
   height: 1px;
   width: 1px;

<div id="preloaded-images">
   <img src="" width="1" height="1" alt="Image 01" />
   <img src="" width="1" height="1" alt="Image 02" />
   <img src="" width="1" height="1" alt="Image 03" />

Thus, as Nanda suggests, it is better to preload images using only CSS. Using the CSS background property, we can preload images via existing <div>s, <span>s, or other elements in the (X)HTML markup.

Let’s say you have three images (e.g., image_01.png, image_02.png, and image_03.png) that you would like to preload using this method. First, examine your markup for three identifiable <div>s (or other elements) that may be used as CSS hooks for the preloaded images. For example, looking at the source code of the current page, I would choose the following three divisions:

  • <div id="wrap">...
  • <div id="jump">...
  • <div id="header">...

Then, to implement the preload, I would add the following code to my site’s CSS file:

div#wrap {
	background: url(image_01.png) no-repeat -9999px -9999px;
div#jump {
	background: url(image_02.png) no-repeat -9999px -9999px;
div#header {
	background: url(image_03.png) no-repeat -9999px -9999px;

Here, we are preloading each image into its own unique <div> and then preventing its display by positioning it to the far left of the browser window. If the preloading elements are empty with no discernible height or width, hiding the preloaded images off-screen should not be necessary because they will not be displayed. Even so, it is probably a good idea to relocate them just to be safe.

Of course, once you have implemented this code to preload your images, they will be immediately available (depending on size) for display in your document as needed. Simply refer to them as normal using whatever CSS code that you would normally use. For example, once these images have preloaded, I could employ the following :hover technique with minimal presentational delay:

a#first:hover {
	background: url(image_01.png) no-repeat 50% 50%;
a#second:hover {
	background: url(image_02.png) no-repeat 50% 50%;
a#third:hover {
	background: url(image_03.png) no-repeat 50% 50%;

According to my tests, this technique works well in any browser (including IE 6) that supports the CSS background property. Best of all, this preloading method is entirely unobtrusive, requiring no presentational code and degrading gracefully in non-supportive browsers.


Jeff Starr
About the Author
Jeff Starr = Designer. Developer. Producer. Writer. Editor. Etc.
The Tao of WordPress: Master the art of WordPress.

29 responses to “Pure CSS: Better Image Preloading without JavaScript”

  1. Thanks for this,

    we did face lot of problems on few sites, where images are used everywhere. I hope using this technique helps me solves the problem.

    Thanks for it.

    Synergy Informatics
    Surat Leading Web Hosting & Design Company

  2. Likewise, SWH, I hope the code works well for you. I tend to post quite a bit about CSS/JavaScript image preloading, and also about image optimization in general. In fact, I have a new article on image preloading that will post soon. Grab my feed for all the action! ;)

  3. Hi Jeff,

    I would like to read more from you… especially compared to other blog, cause your articles are simple & effective and answers the problems in perfect sense.

    Synergy Informatics

  4. I have a similar problem to Lukas (#11 above). I want to display a Flash streaming gallery that uses a list of images (maintained in an xml files). So, the images are streamed/loaded sequentially and this is an improvement over embedding all of the images in the video. But, there is a delay in the display of the large images the first time through the gallery show (while the thumbnails below the large image display instantly).

    The problem is that this may be a large number of images, and embedding the background image statement in dozens of CSS elements is not practical. So, what is the next best technique? Would it be to create a long list at the bottom of the calling page so that the images are loaded before opening the gallery page, along the lines of the example above:

    Thanks for any additional advice.

  5. Jeff Starr 2008/10/19 2:49 pm

    @Rom: Ugh! WordPress gobbled your code! Please repost using <code> tags wrapping each line of code, or, simply email me (Jeff at this domain) directly with the code and I will update (and respond to) your comment.. Thanks!

  6. I say why attaching the background images to some obscure divs, spans etc, when we could declare the neccessary background images on the neccessary elements but just with a big background-position value like -9999px and only change the position on hover or whenever needed.

    Does it make sense?

  7. @Sp.Shut: Yes, makes complete sense — thanks for sharing that great tip with us! :)

  8. Serge Krul 2009/06/09 5:59 am

    just wanted to note that the values must end with a unit like “-9999px”.

    when i tried copy-pasting “-9999” it didn’t work (FF3).

    ah, and thanx for this excellent post! :)

  9. @Serge Krul: Ack! Yes, you are correct – my bad. Units are definitely required for the positioning to work properly. Thanks for the catch. I have updated the article with the correct information. Cheers.

  10. Bathrobewarrior 2009/08/01 7:44 pm

    I’ve heard of doing it this way though I find that a lot of times, it doesn’t seem to noticeably preload. I usually just end up using the 1 image sliding technique for buttons etc. Other images, I just try to make them small enough to not even matter. :)

    I will definitely try this out though!


  11. hey Jeff,

    A few days ago I manage to solve my problem in regards to rotating my homepage photos using your site =D

    thanks again for that again..hope you don’t mind me asking a little bit more in regards to this preloading topic of yours…

    You see, I have about 100+ photos in there with 290X230 dimensions (320×260 when hovered)….I’ve been trying to integrate jquery lazyload plugin into my wordpress for the past two days but it seems like its not working…after reading your image preloading topics, I went to double check my theme’s style.css and its corresponding theme.js file to see if my theme is already using the preloading technique….this is what i found:

    in style.css: {
    /* background:url(img/sampler.jpg) center center no-repeat;*/
           margin:0 1px 1px 0;
           filter: alpha(opacity=0);
           khtml-opacity: 0;

    .blankphoto {
           margin:0 1px 1px 0;

    #highlight {
           float: left;
           border:3px solid #ffffff;


    $(function() {
           var left;
           var top;
           var bgimage;
                  function () {
           var offset = $(this).offset();
           left = offset.left-18;
           if (jQuery.browser.msie) {
                                top =;
           } else {
                                top =;
           bgimage = $(this).css('background-image');
           $('#hack').css({'display' : 'block', 'position' : 'absolute', 'background-image' : bgimage, top : top+'px', left : left+'px'});
           $('#highlight a#highlightlink').attr({
                                href: $(this).children('a').attr('href'),
                                title: $(this).children('a').attr('title'),
                                alt: $(this).children('a').attr('alt')

                  function () {

                  function () {
                  function () {
                         $(this).css({'display' : 'none'});

           $("").each(function (i) {
                         opacity: 0
                  }, 0 )

    Jeff, please can you confirm PLEASE if my theme is preloading the images as background? if so, will jquery lazyload plugin work if the images are already loading as a background??? i tested my website homepage loading time with 42 images and the result is 16 queries in 0.29 seconds, i don’t know if that suppose to be good…i’ve already integrated wp plugin css sprites but want to speed it up some more to accomodate my 100+ images..

    Thanks…sorry for the looong comment..

  12. I’ve been researching this for a while and I think this technique is the most reliable while also not messing with accessibility or creating extra markup. I’d also add that for accessibility it’s usually recommended to put your navigation into an unordered list so the ‘li’ parent elements of the anchors that require hover images are good candidates for the preloading images using this technique (assuming your hover images are for navigation elements and your parent list items don’t require background images themselves).

Comments are closed for this post. Something to add? Let me know.
Perishable Press is operated by Jeff Starr, a professional web developer and book author with two decades of experience. Here you will find posts about web development, WordPress, security, and more »
.htaccess made easy: Improve site performance and security.
Making great strides on my new book. Planned release in December :)
To organize my life, I keep it simple. online: plain text files, offline: sticky notes.
Official list of Googlebot IP addresses.
Lot of 1s in today’s date 20211111.
Working on a new book :)
I enjoy listening to original Star Trek and NG episodes while working online. After a while it feels like I’m working on the ship as part of the crew, going on adventures.
New version (2.6) of my shapeSpace starter theme now available! Always free & open source for everyone :)
Get news, updates, deals & tips via email.
Email kept private. Easy unsubscribe anytime.