spam poison

Delay loading your print CSS and JS includes - the saga continues

This acticle in other languages: Russian

Optimization of several CSS and JS files


Delay loading your print CSS

So you have two stylesheets on your page, like this:

<linktype="text/css"rel="stylesheet"href="screen.css"
 media="screen"/>
<linktype="text/css"rel="stylesheet"href="print.css"
 media="print"/>

The first one is used to render the page on screen, the other one is used for printing the page and print previewing it. Good.

The thing is, when it comes to performance, the browser won't render any part of the page, until all stylesheets are downloaded (fiddled with here). That includes, unfortunately, stylesheets not designed for the currently rendered media. In other words, the browser won't display your page, until the print stylesheet is also downloaded, although it's not used at all for displaying the page. That sucks and should really be addressed in future browser versions.

Test

I did a test page to check this, it's here - print.php. It includes two stylesheets, the first one intentionally sleep()s for 5 seconds, the second one - for 10 seconds.

The result is that in both Firefox and IE it takes 15 seconds for this page to be rendered. Here's the Firebug picture:

media-print.png

In Safari on Windows, it only took 10 seconds the first time around, as both stylesheets were downloaded simultaneously. Good. The bad is that after refresh, the first CSS was not even requested, I tried it a few times, actually sometimes I got the error "The error was: “unknown error” ((null):10053) ", but hey, this is the first release of the browser, it can't be perfect. Actually after I shut down Fiddler, which is what I used to monitor the HTTP traffic, the page was back to normal, so it's not clear who's to blame.

So?

Well, in order to increase rendering performance, all stylesheets not absolutely needed to initially render a page should be loaded after the page load, in the background. Once the user has a fast rendered page to interact with, you can load the additional CSS (and JavaScripts for that matter) in the background, using script and style DOM includes.

Update: From my comment bellow - a better option is to include the print css as part of the main css:
@media print {…}

JS includes - the saga continues…

The problem in question is how to find out a dynamically included JavaScript file is actually loaded. The concept of JavaScript includes is here, the IE-only solution is here. The IE solution is to use the onreadystatechange event that is fired when a new script is included. It also works for dynamically loaded CSS files using a new link DOM element. Thanks to the comment from Björn Graf, I tried using onload event to test if the new script is included using Firefox. It worked!

The code

What we have here (demo) is trying to include a .js file and an .css file, creating new script and link DOM elements. Then I'm attaching event listeners to those new elements - one onload and one onreadystatechange. The script that is included (jsalert.js) has one alert().

var css;
function include_css(css_file) {
    var html_doc = document.getElementsByTagName('head')[0];
    css = document.createElement('link');
    css.setAttribute('rel', 'stylesheet');
    css.setAttribute('type', 'text/css');
    css.setAttribute('href', css_file);
    html_doc.appendChild(css);

    // alert state change
    css.onreadystatechange = function () {
        if (css.readyState == 'complete') {
            alert('CSS onreadystatechange fired');
        }
    }
    css.onload = function () {
        alert('CSS onload fired');
    }
    return false;
}

var js;
function include_js(file) {
    var html_doc = document.getElementsByTagName('head')[0];
    js = document.createElement('script');
    js.setAttribute('type', 'text/javascript');
    js.setAttribute('src', file);
    html_doc.appendChild(js);

    js.onreadystatechange = function () {
        if (js.readyState == 'complete') {
            alert('JS onreadystate fired');
        }
    }

    js.onload = function () {
        alert('JS onload fired');
    }
    return false;
}

Results

As you can probably guess, the results are different in IE and FF.

  • CSS inclusion - IE fires both events, onload first, then onreadystatechange. FF fires nothing.
  • JS inclusion - IE fires onreadystatechange. FF fires onload. Both will execute the script before firing the event.

Conclusion

  1. So there is, after all, a cross-browser way to tell when a JavaScript is actually included and that is to attach two event listeners - onload and onreadystatechange.
  2. In IE you have two ways to tell when a CSS is included.


Article information
Wroted: Tue, 21 Aug 2007 20:53:00 EEST
Autors: Stoyan Stefanov
Added by: AlexParamonov at Thu, 27 Nov 2008 20:55:21 EET
Modified by: AlexParamonov at Thu, 27 Nov 2008 21:12:43 EET
Translation information
This acticle in other languages: Russian
Added by: AlexParamonov at Thu, 27 Nov 2008 21:04:42 EET
Modified by: AlexParamonov at Wed, 03 Dec 2008 16:51:45 EET
Copyright © 2008-2010 Alexander Paramonov
Valid XHTML 1.0 Transitional