Data URIs and Dynamic Stylesheets
This a quick follow-up to a post I wrote a few months ago about embedding data URIs in stylesheets and a potential performance hazard regarding the way non data URI images are positioned in the network waterfall. What I found was that under certain circumstances the performance penalty was great enough to not only negate the benefits of using data URIs for images, but to actually increase the overall load time.
My solution was a hack: put the data URI images in a separate stylesheet and place the link to the stylesheet at the bottom of the page, after a script reference. This caused the browser to render the page after the main stylesheet (the one without the data URI images) and download the data URI stylesheet and other images in parallel. The result was a fast loading, progressively rendered page. But, in addition to producing invalid HTML, it went against the widely accepted best practice of keeping stylesheet references within the
Despite some great discussion in the comments, there didn’t seem to be a decent answer to the problem. I forgot about it for a couple of months and then the other day the very obvious and simple solution came to me.
A quick recap of the original obstacle
When you replace the background image references in your stylesheet with data URIs, the size of your stylesheet increases significantly as the image bits are moved from image files into the stylesheet. (After gzipping, the increase in stylesheet size equals the original size of the images.) The page weight is the same but the difference is that stylesheets block rendering and downloading of images referenced in stylesheets. This means the more data URI images you have in your stylesheet, the bigger your stylesheet, the longer it takes before the page is rendered, and the longer it takes before other non-data URI CSS images can be downloaded. For an example, see how page components 4 and 5 are blocked in the above image.
You might be wondering why we would have both data URI CSS images and regular CSS images. The reason is that IE8 only supports data URIs smaller than 32KB. So if you have an image 32KB+ you’ll need to serve it normally if you want your site to get along with IE8.
My previous hack/solution
One way I found to get around the problem was to move the data URI images into their own stylesheet (yes, adding an additional HTTP request) and force the browser to load the stylesheet asynchronously by placing the data URI stylesheet link in the
body, after an external script reference. As you can see in the waterfall, the data URI images and the regular images are now downloaded in parallel.
A better way
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Title</title> <link href="style.css" rel="stylesheet" media="all" /> <script> var datuURIs = document.createElement("link"); document.head = document.head || document.getElementsByTagName('head'); datuURIs.href = "duri-images.css"; datuURIs.rel = "stylesheet"; document.head.appendChild(datuURIs); </script> </head>
Whether the dynamically inserted stylesheet occurs before or after the main stylesheet does not seem to matter in most browsers, but it does in IE8. In IE8 the dynamic stylesheet will block if it comes before the regular stylesheet. So if you’re using this technique you’ll want to order the elements as I have above.
On the general subject of using embedded data URIs for images, is it worth fussing with all this stuff? In most cases I think the answer is probably not (yet, at least), in part because you can achieve a similar performance result with an aggressive use of CSS sprites. Although CSS sprites have their own overheard and drawbacks, overall I think they’re easier to work with, especially when you start having to worry about data URI workarounds for IE6 and IE7.
But there are certainly situations where data URIs might be a good fit. Mobile applications and “HTML5 apps” aimed at non-IE browsers come to mind. And if nothing else, they’re fun to play with.