2005-11-11

On-demand cross-site javascript

While I am neither firm believer nor much versed in the design patterns camp, they make good, neat vocabular units. It won't become a true lingua franca until enough people in the extended circle (by which I mean the wide public of not very acacemically versed J. Random Hackers) are familiar with them, but I would still encourage reading up on them, at least a little. This post, once I get on with it, will cover some ground on on-demand loading of javascript, which is also the largest common denominator with the ajaxpatterns article I read as inspiration for and background to writing this.

While I believe design patterns by now at least almost are safely out of hype, they were a religious plague a few years ago when present day plagues like AJAX had not yet caught on. I remember reading an article by a frustrated or at least blissfully cynical Jamie Zawinski, probably at gruntle (though I can't seem to find it now), where he patiently explained that design patterns do not make good programmers out of mediocre programmers. The pattern is a concept explained in simple terms, a basic idea, if you will, on how a problem or part of a problem is formulated as a solution, but you won't design high quality software just by having read up on a library of off the shelf patterns. (In case someone would happen to dig up Jamie's article, by the way, you will most likely find that this is my take on the rant, though by all means read Jamies' too -- it was a good read.)

Anyway, the one thing I had expected to find in an article about on-demand javascript but did not find, was a run-down on how to perform cross-domain on-demand loading of javascript code that you have not written yourself. And consequentially, which will not magically plug in with the rest of your pages and find its place somewhere, hooking up with the rest of your code, announcing "I'm live and loaded now!", which is otherwise a very comfy way of writing external modules. In summary: how do we fire an onload callback, once a <script> tag we just added to the document flow has been fetched and parsed?

Nope, you didn't miss it; it wasn't there. I suppose there might have been in the linked Do you want to know more? tutorial article by Thomas Brattli at the end of the page, but that place had unfortunately been defaced by some schmuck who adolescently replaced all content text with 65,000 by 65,000 pixel wide goatse.cx images from web.archive.org and a note deriding Windows IIS security. (I mailed Thomas about it; hopefully he will find the time to restore his site.) Many browsers are known to crash at huge images like that, by the way -- I strongly advise against going there to look for yourself, if you have anything important going in the your browser session (hence my not linking there directly from here).

If we had wanted to load the script from our own domain, we could of course have used XMLHttpRequest, but without that, we are pretty much left to our own devices. As it happens, I stumbled upon one of those devices just that yesterday, in looking for inspiration on how to integrate Del.icio.us JSON feeds for purposes such as making a categories (or tags) side bar, along the lines I touched briefly in yesterday's post. Ma.la (who probably has a better name, but I'm afraid my Japanese isn't what it should be) has made a striking little piece of inginuous code that does just this -- watch this del.icio.us JSON feed dynamic loading demo (usage: type a del.icio.us user name in the input field and click load to add the most recent fifteen bookmarks to the page).

The bold face bits together perform the magic of running the feed_onload callback, once the code has been fetched from Del.icio.us, and the Delicious.posts variable is defined (since that is what you get when you pull in a Del.icio.us JSON feed).

How this magic works? Well, the setup code before the first bold statement (horrible pun, I admit) sets up some scaffolding for a poll loop trying to evaluate a statement 100 times a second until it eventually yields something non-false without throwing an error. A bit smelly, and a bit elegant, both at the same time. (I would have settled for something much less frequent; running every ten milliseconds feels somewhat excessive, but to each their own, I suppose.)

The second bold face statement makes sure the onload callback won't unintentionally get fired again, by deleting the variable the loader is looking for, or else we would get a page growing fifteen new links every ten milliseconds. This code is of course asking for trouble in the event of multithreaded javascript ever happening in browsers, but until then, there is no race condition to fear.

The less dynamic approach I am planning on taking at the moment, only loads the feed once at page load, then writes some code into the page and considers itself done. It does not really qualify as on-demand javascript, but rather multi-source javascript compositioning, which is useful too. I'm talking about loading a Del.icio.us JSON feed on pageload, adding some navigation based on it (such as the categories you see at Greg's Speccy blog, which is implemented just this way), or Yamy 改め Chalice's Del.icio.us sidebar, which lends itself better for an example. (Again, I wish my Japanese was better; it really looks like a good site.)

Chalice simply solves the problem the Web 1.0 way: first include the external script, then your own (in-line or externally, whichever you prefer) which uses the data loaded from the first script. Basic, robust, foolproof. The HTML standard guarantees that both scripts are loaded before they get executed, in document order, and all is fine and dandy.

This will do for a static navigation panel. It works for Greg and it works for Chalice. Personally, though, I'm still curious about the mechanics of dynamic loading, for my later ideas on a dynamic navigation variant, functionally very similar to Greg's, but with unfoldable categories that expand and show links from other categories without reloading the entire page. It's this kind of small AJAXish applications I'm most mirthful playing with, and I'm really looking forward to eventually cracking that nut. What guarantees do browsers offer about the loading and execution order when you inject new script tags into a loaded document? Indeed, what happens when an external script does not load?

It's very hard to track down answers for questions like these by asking Google, and it's not particularly easy by reading standards either -- the W3C HTML standard, for one, is mostly about the "solid state" HTML document approach, and does not address the DOM "one-page HTML application" approach many of us are moving on towards today. Useful pointers to articles on subjects like these are warmly welcome, by the way.

3 comments:

  1. We use a slightly different technique that lets us know when a script is ready and (if you have control over the server output) can inform us and/or users of any error that occurred. Instead of adding the script tag to the existing document we generate a new document (iframe or other frame). Something like this:

    function fRequest(doc,url,cb)
    {
    doc.open();

    var html = "<html> <head>";
    if(cb)
    html+= "<script> function cb("+cb+");</script>";

    html+="<script src='" +url+ "'></script>";
    if(cb)
    html+="</head> <body> onload='cb()'</body> </html>";
    else
    html+="</head> </html>";
    doc.write(html);
    doc.close();
    }


    Then create a document, get the document object for it and call:

    fRequest(doc,'js/bla.aspx','alert("done");parent.doSomethingOnLoad();');

    ReplyDelete
  2. I have a web page that encloses huge amount of javascript code, thus making the page heaview and thereby increasing the page load time.

    On demand javascript is a solution to the problem.
    But i could not make the most out of it

    ReplyDelete
  3. The problem has been solved for JS and CSS asynchronous and synchronous dynamic inserting.
    See http://system.dlv.phpb002.de , read help text on site.
    Cheers, Frank

    BTW JS & CSS are both inserted into head via script and link tags respectively, JS presence detection works through polled checking whether the object exists, CSS is detected same way via document.stylesheet checks.

    The lib supports recursive loading of web objects.

    ReplyDelete

Limited HTML (such as <b>, <i>, <a>) is supported. (All comments are moderated by me amd rel=nofollow gets added to links -- to deter and weed out monetized spam.)

I would prefer not to have to do this as much as you do. Comments straying too far off the post topic often lost due to attention dilution.

Note: Only a member of this blog may post a comment.