Better Image Caching with CSS

by Jeff Starr on Sunday, January 18, 2009 29 Responses

I have written previously on the fine art of preloading images without JavaScript using only CSS. These caching techniques have evolved in terms of effectiveness and accuracy, but may be improved further to allow for greater cross-browser functionality. In this post, I share a “CSS-only” preloading method that works better under a broader set of conditions.

Previous image-preloading techniques target all browsers, devices, and media types. Unfortunately, certain browsers do not load images that are hidden directly (via the <img> element) or indirectly (e.g., via the parent <div> element) using either display:none; or visibility:hidden;. Further problematic is the potential unintentional display of images on pages when presented via specifically designed print stylesheet.

To get around these limitations, we begin by segregating our strategy to target different media types. For example, for web pages featuring both screen and print stylesheets, we treat each separately by writing this:

@media screen {}
@media print {}

Then, to ensure that the images are preloaded in all browsers, we need to avoid the use of either display:none; or visibility:hidden; in the method. Rather than risking non-caching by hiding or preventing the display of images that need preloaded, we ensure their display and position them far outside of the screen. To do this, we enclose all images that need cached within some specifically identified division like so:

<div id="preloader">
	<img src="http://domain.tld/path/images/01.png" width="1" height="1" />
	<img src="http://domain.tld/path/images/02.png" width="1" height="1" />
	<img src="http://domain.tld/path/images/03.png" width="1" height="1" />
	<img src="http://domain.tld/path/images/04.png" width="1" height="1" />
	<img src="http://domain.tld/path/images/05.png" width="1" height="1" />
	<img src="http://domain.tld/path/images/06.png" width="1" height="1" />
	<img src="http://domain.tld/path/images/07.png" width="1" height="1" />
</div>

Then, with that markup in place, we flesh out the previous media directives with the following CSS:

@media screen {
	div#preloader {
		position: absolute;
		left: -9999px;
		top:  -9999px;
		}
	div#preloader img {
		display: block;
		}
	}
@media print {
	div#preloader, 
	div#preloader img {
		visibility: hidden;
		display: none;
		}
	}

Here, we have the preloader division positioned far to the lower-left outside of the screen, and then redundantly specify block display on the image elements.

Finally, to prevent the unwanted display of these preloaded images via print media, we simply hide the images via display:none; and visibility:hidden; declarations.

When using this method to preload/cache images, remember to call the preloaded images by using the exact same path used for the original preloaded image. For caching to work, the browser must reference an existing resource via the identical path.

This method is designed to enable the caching of specified images in virtually all visual browsing devices. If you encounter cases where this method does not work, or if you have comments or suggestions for improvement, please share by leaving a comment below. Thanks!


29 Responses

Add a comment

[ Gravatar Icon ]

Jin#1

Good article Jeff.

One question: what’s the need for visibility:hidden on top of using display:none? Since it does the same as display:none, except for it still takes up white space while being invisible?

Thanks.

[ Gravatar Icon ]

Jeff Starr#2

Hi Jin, we are including both visibility:hidden and display:none to account for a broader range of application. Browsing devices handle CSS properties differently according to specific implementation. Using both properties may be redundant in some browsers, but covers the bases when one or the other is behaves unexpectedly.

[ Gravatar Icon ]

Bill Brown#3

What a great article!

I’m wondering if there should be some inclusion for handhelds. I think they would get a big block of images at the bottom (probably) of the page, which would also likely increase their initial download time, something which one assumes is wise to avoid for handheld devices. I don’t own a Blackberry and if my cell phone is on, it probably means there’s a tear in the space time continuum.

Perhaps the @media print rule could read @media print,handheld?

[ Gravatar Icon ]

Jin#4

Good point Jeff. Thanks for the explanation.

[ Gravatar Icon ]

webbo#5

Sorry if this is a stupid question but I am new to this!!

But if you preloading all the images this way will it not just slow down the load time of the 1st page? This would be more important to load quickly than subsequent pages.

[ Gravatar Icon ]

John V#6

Hi Webbo,

If you add all of these images in a div at the very bottom of the page (i.e. before the ending body tag), then the loading won’t be noticeable to users.

@Jin:
display:none and visibility:hidden are different in nature, in that the display property changes the flow characteristic of the element, whereas visibility doesn’t.

When an element is set to visibility:hidden it is simply invisible and stops receiving events, the element will still be part of the document flow however and as such will still occupy space.

When an element is set to display:none the element is removed from the flow and the following elements move to take its space.

[ Gravatar Icon ]

Dan#7

I tried this for a while, but I was concerned that people using text based browsers might be confused seeing so many images and perhaps feel like they were missing out on content.

I’ve gone back to using sprites for design/interface elements.

Anything else doesn’t really need caching.

[ Gravatar Icon ]

teddY#8

Nice writeup on image caching :) I didn’t realise the importance of the redundancy for visibility and display properties until I read through the comments. In the past when my layouts involve using multiple, small images/icons (usually a typical layout of mine will have around 20~30 images), I will preload them using Javascript in an array or something. Then when I became more well versed with CSS (and when my javascript started a downward spiral into oblivion), I used the visibility/hidden trick. It never came across me to use absolute positioning to make sure that they stay out of the page though :) that’s interesting!

For the moment being I’ve decided that having too many images for a layout does not only make naming them a big problem, but also create unnecessary hassles for updates/migration. I hate jumping between the image folder and CSS (or notepad). I’ve settled for CSS sprites for now.

But then again, a very informative piece of post on image caching! I love it that you’re using a purely CSS way to do that instead of incorporating another snippet of javascript (for some reason I try to avoid inserting too much javascripts into my pages). Call me a purist.

p/s: Sorry to hear about the subscriber’s issue. Is it a bug or something?

[ Gravatar Icon ]

Jeff Starr#9

@Bill: Using @media print, handheld is an excellent idea. Great tip for optimizing mobile design!

@webbo: To my knowledge, placing your preloaded images at the bottom of the page causes them to load after the remaining elements on the page. Thus, visitors will be able to view contents while images load in the background. This technique works great for slideshows and other thumbnail/full-size galleries.

@Dan: Every design is unique and requires different strategies. Using sprites for slideshows and product pages may not be the optimal way to go. Preloading and caching images will definitely help in these scenarios.

@teddY: Thanks for the great comment! I too am still learning about all the details and sorcery involved with preloading and caching images. Anything related to increasing performance is on my “favorite topics” list ;) Also, not sure about Feedburner, but I hope the higher count is restored! ;)

[ Gravatar Icon ]

Donace#10

Hey i’m not going to repeat what others have said (except woohoo great article ;) )

1) However say you move the theme / blog to a new server or location, This would require updating then right?

2) Reading around i’ve noticed scripts that cache css js etc on the fly to a seperate directory (http://reinholdweber.com/); So was wondering could you set this as well with a similar principle? i.e. it caches ‘theme’ images and ‘post’ images in a similar manner. (I too love perofrmace :p)

there are one two other points but i’ll get back to them after lunch and saying…new design?!;)

[ Gravatar Icon ]

Jeff Starr#11

@Donace: Yes, as with all markup, links, and images, if you relocate your site, the paths will change; and if they do, URL paths will need to be updated with the correct information. Throw me a link to the second topic you mention and I will have a look.. at the moment it’s not ringing any bells..

[ Gravatar Icon ]

Donace#12

I can’t recall where I initially read it but http://reinholdweber.com/?p=42 is where I last enquired about it :p

and, here is the script (partial) http://reinholdweber.com/?p=37

[ Gravatar Icon ]

Jeff Starr#13

Thanks Donace, will check it out! :)

[ Gravatar Icon ]

Cooltad#14

Instead of setting an outrageous position, why not have a position of 0 0 and set the size of the image to 0 0?

And you should improve this further to make it hidden even when a person deactivates css on your website. Especially with how easy it is on Firefox.

[ Gravatar Icon ]

John#15

If I use css background images instead of calling them via will it be cached too?
In other words: If I use sprites, should I call the image using at least once so it is cached?

[ Gravatar Icon ]

John#16

Posting the question again since it eats the html tags:

If I use css background images instead of calling them via the “img tag” will it be cached too?
In other words: If I use sprites, should I call the image using “img tag” at least once so it is cached?

[ Gravatar Icon ]

Jeff Starr#17

@Cooltad: Some great ideas there. Will have to check them out in cross-browser environment. Not sure about leaving the images at 0 0, but the logic is sound if image size sticks at nothing. For your second point, are you referring to inline styles? If so, I would rather not..

@John: If you are able, keep your sprite size small and optimized well enough to avoid the need for preloading/caching. Even so, images are cached according to file path. Use the same path as the cached images for CSS backgrounds for successful preloading.

[ Gravatar Icon ]

Peter Assenov#18

hi, I think CSS image cashing as shown here could be really useful…
I’m using another technique that can work together with this one.
In case you have many small images in the website /icons or thumbnails/ the page loading speed is going down because of the multiple HTTP requests for every one of them. Cashing doesn’t seem to help a lot because the problem is not the size, but the number of images/connections for them/. Even when the browser retrieved the images from the local cash still there is a slowdown.
In this case you can merge all icons/thumbnail/ in a large image and use background-position to show the appropriate part of it. Of course you can cash the large image with CSS. I’ve noticed that when the images are more than 100 in a page, merging usually helps.
Hope it will be useful…
cheers.

[ Gravatar Icon ]

Jeff Starr#19

@Peter Assenov: Thanks for the tips! You raise and excellent point about reducing the number of HTTP requests by using sprites. It is an excellent optimization technique that I use here at Perishable Press all the time. For example, if you scroll up to the top-right sidebar panel and view the background image for any of the small icons, you will see how I managed to reduce around thirty HTTP requests for images down to one.

[ Gravatar Icon ]

Peter Assenov#20

@Jeff I think you did a great job for Perishable Press. I really like the front-end.

Also using sprites overcomes the annoying background-image blinking in IE.
With a little scripting can nice hover animations can be made- http://snook.ca/technical/jquery-bg/, but that’s getting off-topic, sorry.

[ Gravatar Icon ]

Josh#21

Well this is definitely a great way to preload images for subsequent pages on a site… I suppose using a sprite for the main page could cure load time for index, then using this method would resolve load time on any further pages.

I have a question (somewhat off topic) now that I’ve mentioned sprites… Has anyone else had troubles with IE 7 becoming extremely “jumpy” when you scroll down the page if you’re using a sprite for background images (repeating x and/or y)?

[ Gravatar Icon ]

Sunny Singh#22

I don’t see any use for this, ever since I started using sprites especially for hovers, there is no need to preload images.

[ Gravatar Icon ]

Thierry Koblentz#23

In my humble opinion, this is not caching images with CSS, but caching images with CSS *and* Markup ;-)

Why the DIV? Why the images?
Why not simply choosing a few elements on the pages that do not have a background image and style them with the images we want to preload (with background-position:100% 100%).

If you believe this is not as good because the call is made early (in the HEAD) rather than late (end of BODY), then use simple DIVs rather than images.
That image markup is a lot of bytes. Besides, images are content, and your example would create issues with screenreaders. Because without a ALT attribute a screenreader speaks the name of the image.

[ Gravatar Icon ]

Greg#24

Good post, thanx !
I made a post about 2 ways to cache JavaScript, CSS and image only : Using your browser’s cache : here : http://bit.ly/4U4p5U

[ Gravatar Icon ]

Christopher#25

Brilliant article, very helpful.
Thanks

[ Gravatar Icon ]

Jeff Starr#26

@Greg: That’s a great article - thanks for sharing. I recently posted a follow-up article that includes your method. You can check it out here. Cheers!

[ Gravatar Icon ]

Ciprian#27

Thank you for this article, it works great.
Finally Internet Explorer flickering is gone!

Trackbacks / Pingbacks
  1. Better Image Caching with CSS | ReachCustomersOnline.com
  2. Better Image Caching with CSS • Perishable Press | CSS Tutorials - CSSHelper.net
Share your thoughts..

Read Comment Policy

Comment Rules: No spam. No profanity. Use your real name. You may use simple HTML tags for style. Wrap all code in <code> tags. Learn more.



Attention: Do NOT follow this link!