HIGH PERFORMANCE WEB SITES BLOG

Onload in Onload

September 12, 2014 7:58 am | 4 Comments

or “Why you should use document.readyState”

I asked several web devs what happens if an onload handler adds another onload handler. Does the second onload handler execute?

The onload event has already fired, so it might be too late for the second onload to get triggered. On the other hand, the onload phase isn’t over (we’re between loadEventStart and loadEventEnd in Navigation Timing terms), so there might be a chance the second onload handler could be added to a queue and executed at the end.

None of the people I asked knew the answer, but we all had a guess. I’ll explain in a minute why this is important, but until then settle on your answer – do you think the second onload executes?

To answer this question I created the Onload in Onload test page. It sets an initial onload handler. In that first onload handler a second onload handler is added. Here’s the code:

function addOnload(callback) {
    if ( "undefined" != typeof(window.attachEvent) ) {
        return window.attachEvent("onload", callback);
    }
    else if ( window.addEventListener ){
        return window.addEventListener("load", callback, false);
    }
}

function onload1() {
    document.getElementById('results').innerHTML += "First onload executed.";
    addOnload(onload2);
}

function onload2() {
    document.getElementById('results').innerHTML += "Second onload executed.";
}

addOnload(onload1);

I created a Browserscope user test to record the results and tweeted asking people to run the test. Thanks to crowdsourcing we have results from dozens of browsers. So far no browser executes the second onload handler.

Why is this important?

There’s increasing awareness of the negative impact scripts have on page load times. Many websites are following the performance best practice of loading scripts asynchronously. While this is a fantastic change that makes pages render more quickly, it’s still possible for an asynchronous script to make pages slower because onload doesn’t fire until all asynchronous scripts are done downloading and executing.

To further mitigate the negative performance impact of scripts, some websites have moved to loading scripts in an onload handler. The problem is that often the scripts being moved to the onload handler are third party scripts. Combine this with the fact that many third party scripts, especially metrics, kickoff their execution via an onload handler. The end result is we’re loading scripts that include an onload handler in an onload handler. We know from the test results above that this results in the second onload handler not being executed, which means the third party script won’t complete all of its functionality.

Scripts (especially third party scripts) that use onload handlers should therefore check if the onload event has already fired. If it has, then rather than using an onload handler, the script execution should start immediately. A good example of this is my Episodes RUM library. Previously I initiated gathering of the RUM metrics via an onload handler, but now episodes.js also checks document.readyState to ensure the metrics are gathered even if onload has already fired. Here’s the code:

    if ( "complete" == document.readyState ) {
        // The page is ALREADY loaded - start EPISODES right now.
        if ( EPISODES.autorun ) {
            EPISODES.done();
        }
    }
    else {
        // Start EPISODES on onload.
        EPISODES.addEventListener("load", EPISODES.onload, false);
    }

Summing up:

  • If you own a website and want to make absolutely certain a script doesn’t impact page load times, consider loading the script in an onload handler. If you do this, make sure to test that the delayed script doesn’t rely on an onload handler to complete its functionality. (Another option is to load the script in an iframe, but third party scripts may not perform correctly from within an iframe.)
  • If you own a third party script that adds an onload handler, you might want to augment that by checking document.readyState to make sure onload hasn’t already fired.

4 Comments

recent posts:

Resource Timing practical tips

August 21, 2014 12:45 am | 7 Comments

The W3C Web Performance Working Group brought us Navigation Timing in 2012 and it’s now available in nearly every major browser. Navigation Timing defines a JavaScript API for measuring the performance of the main page. For example: // Navigation Timing var t = performance.timing, pageloadtime = t.loadEventStart – t.navigationStart, dns = t.domainLookupEnd – t.domainLookupStart, tcp = t.connectEnd – […]

Velocity highlights (video bonus!)

July 28, 2014 11:08 pm | Leave a comment

We’re in the quiet period between Velocity Santa Clara and Velocity New York. It’s a good time to look back at what we saw and look forward to what we’ll see this September 15-17 in NYC. Velocity Santa Clara was our biggest show to date. There was more activity across the attendees, exhibitors, and sponsors […]

HTTP Archive – new stuff!

June 8, 2014 10:11 pm | 8 Comments

Background The HTTP Archive crawls the world’s top 300K URLs twice each month and records detailed information like the number of HTTP requests, the most popular image formats, and the use of gzip compression. We also crawl the top 5K URLs on real iPhones as part of the HTTP Archive Mobile. In addition to aggregate stats, […]

BOOKS

Even Faster
Web Sites

by: Steve Souders
website
Web Performance
Daybook Volume 2

edited by: Stoyan Stefanov
website

TWEETS

PROJECTS

Talks

Sep 30, SF
Oct 11, Amsterdam
Oct 27, SF
Nov 17-19, Barcelona

Videos