A Look at How Browsers Download and Render CSS Background Images

css-images-leadLast week I wrote about using data URIs in stylesheets and how parallel downloads could be negatively affected in certain situations. The tests I ran got me thinking about background images, particularly how the browser downloads them in relation to the other components. After some Googling I was suprised to find very little information on the subject. There’s lots of great research by Yahoo, Google, Steve Souders, and others, on parallel downloads, parser blocking, placement of scripts and stylesheets, etc; but none of it seemed to touch on background images specifically.

What’s so special about background images? Well, I think we’ll find that in most modern sites and applications CSS background images account for a significant number (if not a majority) of the HTTP requests. Exceptions would be sites with heavy inline image content or sites with an aggressive approach to CSS sprites.

So to learn more about it I went out into the field, set up test pages for a few different scenarios, and ran them through a handful of browsers.  Here’s what I found out.

How things usually work

First, let’s just review the usual scenario. If you’re following performance best practices you want to have your stylesheets in the HEAD so that they’re downloaded as soon as possible. The reason for this is that, in most cases, the browser waits to build the rendering tree until all stylesheets have been downloaded. The sooner you get the CSS downloaded, the sooner the browser can start painting the page.

Where images come in

Inline images are not blocked by stylesheets and can begin downloading as soon as they’re encountered in the HTML. CSS background images, on the other hand, are downloaded only when the referring stylesheet has finished downloading. CSS images are kicked off not in the order in which they appear in the CSS but in the order in which they’re called in the HTML. I’m not sure of the precise moment when the download is initiated, but my guess is that it happens just after the CSS rules have been matched, when the property values are assigned to the DOM elements. Maybe there are some browser engineers out there who could speak to this.

Depending on the browser and the conditions of the page, the exact manner in which the CSS images are handled varies. For example, in the aforementioned scenario where the stylesheets are placed in the HEAD, CSS images are not requested until all of the stylesheets have finished downloading (see Scenario 1 below).

On to the tests

Each test page includes two stylesheets. The first declares 10 background images. The second is set to sleep for 5 seconds. The second also sets the background color to a dark grey so we can see on the page when it’s finished downloading. Four page variations were tested in FF 3.5, Safari 4, IE7, IE8, and Chrome 3, using a stylish combination of Fiddler, Charles, and HttpWatch. All loads assume an empty cache.

Scenario 1, stylesheets in HEAD

css-images-scenario1

As you can see, all of the images referenced in the first stylesheet have to wait for the second stylesheet to finish downloading. This is true of all five browsers I tested.

Note: In this scenario as well as the next, Chrome’s Developer Tools Resources timeline indicates that the images are not blocked by the second stylesheet. This is incorrect. I fell for it at first but upon checking in Fiddler it was clear that the requests for the images are not made until the second stylesheet has finished downloading. Nice try, Chrome.

View Scenario 1 Test Page

Scenario 2, first stylesheet in HEAD, second stylesheet in footer

css-images-scenario2-1

css-images-scenario2-2

FF, Safari, and Chrome perform as they did in the first scenario. CSS image downloads are blocked until the large stylesheet in the footer finishes downloading.

IE7 and IE8 begin downloading the CSS images referenced in the first stylesheet once that stylesheet has been received. Note that even though the images are downloaded early the page is not rendered until the delayed footer stylesheet has finished downloading.

View Scenario 2 Test Page

Scenario 3, first stylesheet in HEAD, second stylesheet in footer, external Javascript in between

css-images-scenario3

Without a doubt this was the most interesting scenario. The set-up is the same as the previous one except we’ve sandwiched an external Javascript between the stylesheets (in the footer, just above the second stylesheet).

In all of the tested browsers the CSS images are kicked off as soon as the first stylesheet has finished downloading. No blocking occurs. Note the difference in the total transfer time.

What’s interesting is that in all of the browsers except IE8, the page is rendered immediately after the first stylesheet has been received. The external stylesheet seems to trip up the browser, causing it to construct the rendering tree well before the second stylesheet has been received.

As you can see by the background color in the test page, this causes the familiar FOUC (Flash of Unstyled Content) problem. But, at least from a visual standpoint, this double rendering doesn’t have to be a problem if the styles are coordinated correctly.  For instance if you placed certain background images or styles for elements beneath the fold in your footer stylesheet.

Unfortunately, for uniformity’s sake, IE8 does not agree with the other browsers. It downloads the images early but displays the anti-progressive-rendering white screen until the second stylesheet finishes up.

View Scenario 3 Test Page

Scenario 4, first stylesheet in HEAD, second stylesheet in footer, inline Javascript in between

Same as the last, but with inline Javascript instead. FF is the only one that reacts to the inline Javascript. It performs as it did in scenario 3. The rest of the browsers perform as they did in scenario 2. Not much to report here.

View Scenario 4 Test Page

What’s it all mean?

At this point I haven’t sussed out the extent of the real world significance, if any. I’m mostly just running experiments and posting results. If your site or application has lightweight CSS and some inline images, the performance implications of this are probably nil. On the other hand, if you’re working with large CSS files (as you might if you use data URIs for CSS images), it may be worth taking a look at how to get the CSS images kicked off earlier in the waterfall.

Have any thoughts?  Please hit up the comment form below.

Both comments and pings are currently closed.

Discussion

This is great stuff.

In my experience, the most common setup is >2 stylesheets in the head and scripts at the bottom of the body.

What do the waterfall look like for that scenario?

Nice blog thanks for sharing …. i like it :)

Thanks Paul!

I agree that stylesheets in the top and javascript in the footer is definitely the most common set-up. I actually had a test page for this scenario initially set up but for some reason I left it out. Basically if all of your stylesheets are in the HEAD, the JS doesn’t have an effect on when the background images are requested. So, in all browsers it plays out like Scenario 1 – requests for background images are not made until all stylesheets have finished downloading.

Here’s a quick summary waterfall I put together for that scenario.

Very good article! thanks for sharing your results!

Thanks for sharing! Really interesting post!

Thanks for this great study.

I would be very interested to know how different media types affect these results.

For example, suppose the stylesheet with sleep() has its media type set to “handheld”. The question then becomes “does this stylesheet block the image downloads even though it isn’t used?”

@Josh
That’s an interesting question. I’ll see if I can work that in to the next round of tests. Thanks!

Rob,

fyi, I tested the scenario 3 page twice in IE7 on Vista, empty cache, and both times the screen is white until the seconds css file finishes downloading.

@Aaron

Thanks for the info. This may have to do with IE’s “blank white screen” problem. Reloads and pages opened in new windows render differently than pages opened via link, bookmark, or direct entry in the address bar.

OR, it could be a Vista vs XP thing. My tests were done on XP.

Do you have any information on if a browser will download all the images referenced in a stylesheet, even if they are not used on the page?

I am starting to think that all “background-image” CSS rules are interpreted by the browser (and the image is downloaded) even if they are not required. This is at least the impression I get from the Pingdom tools: http://tools.pingdom.com/ and from the page speed results in Google Webmaster tools.