Browsers cannot start rendering till all Stylesheets loaded. Visitors see blank page (or previous page) during loading of CSS so it’s especially important to load these components faster.
If you have many Stylesheet components, then visitors will have to wait more because of HTTP requests overhead.
Let’s look what can be done to reduce time to render.
1. First, we want to move CSS components to the HEAD section of HTML document so the Stylesheet will be loaded with first priority.
Worse case is when CSS included on the bottom - in this case browser waits till whole document including all components loaded before start rendering. The same thing may happen when CSS included with @import directive, so avoid using @import.
2. If we have several Stylesheets we join them into one bigger CSS file. As described in the previous post about Image Sprites, we want to reduce amount of downloaded components because of HTTP request overhead. And if in case of images there is just a delay before loading next image, in case of Stylesheet it’s a worse case: visitors see blank page until all Stylesheets loaded. One single file loads faster because there will be minimal overhead and usually it compresses better too.
3. We can go even further: if user comes with empty cache we can avoid additional request by including Stylesheet required for the page into HTML. So there will be no requests for CSS at all during loading of the page.
We can preload Stylesheet after the page loaded so for primed cache page views user already has the Stylesheet cached. This way we reduce download size and avoid HTTP requests.
More detailed:
Set a cookie when visitor comes for first time. Check the cookie on every request to determine whether the visitor comes first time or not. This way we can guess cache state of the visitor. Don’t inline Stylesheet component if visitor comes with primed cache, use an external definition instead.
Postload the external Stylesheet component when visitor comes first time (without the cookie). See example below.
Cookie named “jsl” will be set when user sees this page for first time and next requests will use external Stylesheet component.
You can set such cookie in PHP:
$is_first = !isset($_COOKIE['jsl']);
if ($is_first)
setcookie(’jsl’,1,time()+3600*24,’/',’.site.com’,false,true);
We’ve supposed that browser cache data will be removed in 24 hours and cookie expiration time set to 24 hours.
<?php if ($is_first): ?>
<style>
<?php readfile('style.css') ?>
</style>
<script>
function preload_components()
{
var ste = document.createElement('link');
ste.rel = 'stylesheet';
ste.type = 'text/css';
ste.href = 'http://www.site.com/style.css';
document.getElementsByTagName('head')[0].appendChild(ste);
}
</script>
<?php else: ?>
<link rel=”stylesheet” type=”text/css” href=”style.css”>
<?php endif ?>
The block above will output inline Stylesheet in case user comes for first time otherwise it will output link to the static Stylesheet components. You should place this code inside document head.
It will work correctly even in case user comes with empty cache (for example if our cache got forced out because cache size limit).
Style.css - global CSS file. Probably in real application instead of readfile(’style.css’) you will include only part of the Stylesheet that’s needed to render current page.
To find out CSS that’s really required for current page, you can use a Firefox extension: Dust-Me Selectors (note that this tool may not count CSS rules for elements you assign dynamically in Javascript).
Replace existing opening
tag<body …>with
<body … <?php if ($is_first) echo 'onload="preload_components()"'?>>We’ve added document onload event handler that will create link DOM element if user comes with empty cache so the components will be already in cache for further page views. The external component will be preloaded only after whole page loaded and rendered so it won’t hurt performance.
5 comments ↓
At least one browser (Opera) renders as soon as it receives content, even it hasn’t received CSS yet. I’m not sure about the others, but that’s certainly something that they could optimize.
Anyway, isn’t this all made redundant by the fact that HTTP and most browsers support keep-alive? That already removes the overhead of creating new TCP-IP connections for every resource on the same server.
Seeing that, I don’t understand the benefit of inlining the stylesheet on first load. Not only you’re mixing in javascript and cookies, which both can be turned off, and you’re making assumptions about my caching preferences, but you’re actually downloading the stylesheet twice (once inlined, then again postloaded) - actually using twice the bandwidth, just to save a couple of bytes on the HTTP headers. What’s the point?
Ilia,
The overhead is caused by HTTP requests (not TCP-IP connection - yes, keep-alive helps here).
In case of several small files, big part of load time will be spent doing HTTP requests.
Unfortunately, both IE and FF block rendering till all CSS files are loaded. So the problem is still actual.
I would think that if the server can’t handle serving a bunch of static files over the same stream quickly enough, you’ve got bigger problems. Response time (up to the point that the server starts sending data) should be a few miliseconds for static files.
The main delay would still be from sending the css content over the wire - which you’re doing either way if you embed the file. The only thing you save is transmitting a couple hundred bytes that would have been used on the HTTP headers for the extra request, the time of which could be potentially as much as the overhead of calling into PHP to load the stylesheet.
Not to mention that embedding the stylesheet would cause longer load times for browsers that do show content as soon as it’s received (Opera).
Do you have any actual benchmarks on this having a measurable effect that would justify the work?
Ilia,
For ADSL connection the overhead is 70-300ms for each HTTP request depending on hosting and client location.
In practice, browser can spend about 50% of time (or even more) sending requests and waiting. I’m preparing next blog post with illustrations what’s happening during page load.
Impact of embedding stylesheet is another story for another blog post.
Browsers have not so large disk cache by default (Opera 9.60 has 20MB - probably not covering even daily traffic).
Analyzing http logs so far I have seen that usually cache objects live in cache no more than 1-2 days.
[...] ← Stylesheet Composition [...]
Leave a Comment