Jul 232008
 

Stoyan Stefanov writes about Non-Blocking JavaScript Downloads on the Yahoo User Interface Blog.  Good content all around, but I’d like to add a note on an item he somewhat casually skimmed over.

In the “Dependencies” section of the article, Stefanov describes using the onLoad or onReadyStateChange events of the dynamically generated script tag to receive notification when that script has been loaded by the browser.  Once you know it’s loaded, then you know it’s safe to begin using the code it contains.

This works great for Firefox and IE, but fails completely in the Safari browser.  Safari doesn’t implement onLoad notifications for script tags.  This forces you to abandon the onLoad technique and instead use the technique of embedding something at the end of each script file to signal when the file has been loaded. 

The script libraries for the Windows Live sites use this technique – every source file contains a function call at the bottom that tells a central notifier that it has been loaded.  Other code can ask the notifier to signal them when a file or set of files have been loaded.  This works in all browsers without relying on diverging browser idiosyncracies. 

Unfortunately, this also requires that all the JavaScript you want to load dynamically (using this technique) knows about your particular load notification pattern.  If you want to dynamically load JavaScript from multiple libraries or authors, you will either end up with multiple notification systems (which should coexist peacefully) or you’ll have to modify those files to use your load notifier.  This is bad from a design standpoint because it requires that all your code modules have carnal knowledge of your application - erodes the modularity of the source files – or require that all your code modules follow the design patterns of a particular JavaScript toolkit, like DoJo or Prototype or YUI.  The latter isn’t a terrible tradeoff except that it limits your options.

 Discovered via a note by Steve Trefethen on FriendFeed.

  6 Responses to “Cross-Browser Dynamic JavaScript Loading”

  1. [...] Thorpe wrote some comments and the issue with Safari: This works great for Firefox and IE, but fails completely in the Safari [...]

  2. [...] Thorpe wrote some comments and the issue with Safari: This works great for Firefox and IE, but fails completely in the Safari [...]

  3. [...] Thorpe wrote some comments and the issue with Safari: This works great for Firefox and IE, but fails completely in the Safari [...]

  4. CITE:
    This works great for Firefox and IE, but fails completely in the Safari browser. Safari doesn’t implement onLoad notifications for script tags. This forces you to abandon the onLoad technique and instead use the technique of embedding something at the end of each script file to signal when the file has been loaded.
    :/CITE

    I got around the problem of in-code notificators using a namespace-related coding pattern, and an generic interval checking procedure. As to loading foreign libs not following that scheme, I allow for generic checking functions for the interval. Without the checking functions it simply acts as a predictive loader…

    Here is a piece of code, that illustrates the functionality:

    application.vBody = (function(){
    var divid = ”;

    function display() {
    // follow up actions.
    document.getElementById( divid ).innerHTML = tb.loader.tplget(‘application’,’vBody’);
    tb.element.show( ‘toprightcontainer’ , ‘application’, ‘vUserlogin’ );
    $(‘#twoBirdsLogoText’).fadeIn(1000);
    $(‘#twoBirdsLogoText2′).fadeIn(2000);
    tb.timer.add( setuserstats, 3000 );
    }

    function followup(){ // after requirements have loaded
    //predictive load
    tb.require.add([
    [ 'js', 'application', 'submenu' ],
    [ 'css', 'application', 'submenu' ],
    [ 'tpl', 'application', 'submenu' ],
    [ 'js', 'application', 'window' ],
    [ 'css', 'application', 'window' ],
    [ 'tpl', 'application', 'window' ]
    ]);
    }

    tb.require.add(
    [
    [ ‘js’, ‘foreign’, ‘jquery-1.2.2.pack’,
    function(){ return ( typeof window['jQuery'] != ‘undefined’ ? true : false ); },
    function(){ tb.loader.jsload( ‘foreign’, ‘jquery.dimensions’ ); }
    ],
    [ 'js', 'application', 'cUserstats' ],
    [ 'css', 'application', 'vBody' ],
    [ 'tpl', 'application', 'vBody' ]
    ],
    function(){ application.vBody.isready = true; followup(); }
    );

    return {
    isready: false,

    init: function( pDivId ){
    divid = pDivId;
    if ( application.vBody.isready === false ) {
    tb.observer.observe(
    ‘application.vBody’,
    ‘isready’,
    display
    );
    }
    else {
    display();
    }
    }
    };
    })();

    tb.require.add obviously adds elements into the on-demand loading chain. It can be called anywhere in the code, and optionally accepts a callback. The jQuery part of it shows how generic checking is implemented.

    I hope this adds some info to the theme…

    Cheers,
    Frankie

  5. Oh, and BTW it works completely asynchronous and checks for double requirements also, so that nothing is loaded twice.

  6. [...] des feuilles de style en tenant compte des dépendances, sans bloquer le navigateur. Visiblement tout n’est pas clair sur la situation de Safari, il faudrait que je fouille un peu pour tirer ça au clair. D’ordinaire la politique est [...]

Sorry, the comment form is closed at this time.