Wednesday, May 7th, 2008

Lazily load functionality via Unobtrusive Scripts

Category: JavaScript, Prototype

<p>David Kees has written about Using Prototype to Load Javascript Files, which is an implementation of the general technique of loading functionality via scripts based on the availability of DOM elements.

He started using the technique to scratch an itch:

The calendar on this site only appears on pages that show blog-related information. That calendar is enhanced with Javascript allowing you to change the month displayed by the calendar without reloading the rest of the page. So, in order to ensure that these enhancements would be available everywhere the calendar is, I figured I had two options:

  1. Code the inclusion of the Javascript file into every page which requires it. While a good solution, and one that has worked well in the past, sometimes it can be difficult to remember or realize that you’ve forgotten to include the file when you add new pages that require it.
  2. Find a way to automatically include the file when it’s needed. This would avoid the need to remember the need to include it when adding new pages that require it, but would result in a little more Javascript going on when the page loads.

Here is an example implementation:

javascript
< view plain text >
  1. document.observe("dom:loaded", function() {
  2.     var calendar = $("calendar");
  3.     if(calendar) {
  4.         var script = new Element("script", { type: "text/javascript", src: "/path/to/calendar.js" });
  5.         document.observe("calendar:loaded", function() { new Calendar(calendar); });
  6.         $$("head")[0].insert(script);
  7.     }
  8. });

Related Content:

15 Comments »

Comments feed TrackBack URI

Interesting but seems like it would have a decent delay. You are waiting until the entire DOM is loaded before adding the script VS having the script download in parallel. You are solving a problem for the developer by creating a problem for the user. Never a good thing IMHO.

For me I usually solve this via whatever backend framework I am using. For example in Rails I might have something like:

#### In my layout file #####

#### In some view where the calendar is used #####

#### In my application controller #####
before_filter :init_head_vars
def init_head_vars; @javascripts = [] end

This is obviously just an example but the concept should be clear. We have the same separation of concerns as the JavaScript method but without the penalty to the user. Obviously it is back-end dependent but with such simple code who cares.

Comment by Eric Anderson — May 7, 2008

Well look like the comment formatting ate my example but I’m sure you get the idea. The backend should be able to solve this problem for you.

Comment by Eric Anderson — May 7, 2008

I haven’t seen a delay on the user’s side, actually. Probably because the calendar.js file right around 2kb so downloading and interpreting it doesn’t take up too much time. Unfortunately, I’m not sure I understand your example at all, but that’s okay since I do understand what you’re getting at with respect to avoiding slowdowns on the client-side of things.

In the past, I’ve also used PHP classes to write the appropriate HTML for times when a specific javascript is required, but I never liked the obtrusiveness of the way it worked.

Either way, thanks for the comment. And thanks, Ajaxian, for showing off my page.

Comment by Dashifen — May 7, 2008

It’s certainly an interesting experiment, but perhaps a little too experimental for my taste. I’d probably go nuts trying to debug stuff, and not having a clear picture of exactly which scripts got loaded in which order.

Keep on experimenting, as it’s a very valuable tool for discovering new ways of making life a little better!

Comment by MorganRoderick — May 7, 2008

Cool, good work :)

Comment by jdalton — May 7, 2008

I find this idea interesting, because using this approach you can load code on demand. The little delay involved in code fetching from the server is compensated by having much less scripts loaded in the page head, and cache reduces this time even further for subsequent pages requests.
We just have to remember the DOM doesn’t complete until all the javascript in the head gets executed, and many times the objects created by the code aren’t used at all. For example using this code you can have a really complex “Ajax” search script load up on demand only if the user starts to use the search widget, instead of having to load and execute lines and lines of code on each page load for a function used only a fraction of the time. Multiply this for each widget a page can use and you see the problem, the head gets cluttered of script requests.
Sure, you can partially resolve the problem on the backend, by writing code in Ruby On Rails, PHP, ASP that writes the appropriate script and CSS requests for each page of a site, but you still have to load and execute a script even if the corresponding function of the page is rarely used. And if the script is heavy on loading and executing (think Google Maps) you really want to load it on demand.

Comment by giank — May 7, 2008

This does work, but the delay is a factor. In order to make it work properly, you can only reference the loaded script once you are certain the script has finished loading. You can do this with callbacks and events (FF, IE, Safari 3+) or timers (Safari 2).

Comment by bobmatnyc — May 7, 2008

Doesn’t scriptaculous already do this?

Comment by Shawn — May 7, 2008

Sure, you have to provide a callback for script loading. That’s why, for example, Google Maps couldn’t be lazy loadable until recently, where they added the ability to callback a function when everything is loaded and executed. Also the scripts need to be slightly changed, many append a function call to the windows onload event, smarter ones detect the DOM load. Now they need to detect the lazy load and execute immediately with a callback.
I think the delay is not a factor, we have the page loaded faster, because we load less scripts upfront. Sure, we move this delay on the actual user action, but at that time, the browser loading pipelline is almost empty, remember the specs restrain download parallelism to 2 requests at the same time, and IE 8 bumps up this value to 4 if I remember well, so parallel loading is not always a good option, it can even slow down an application load.

Shawn: scriptaculous uses the prototype functions to do AJAX and AHAH calls, if I understand, you’re proposing to use the javascript automatic code evaluation of these functions. That’s not the same, realtime code evaluation is slower, harder to debug and many times a potential security breach, as many said I also think eval() is evil.

This solution instead appends a script to the DOM. The only think I would change is the timing the script gets appended, it’s almost useless. Is it more interesting to load the script on demand when needed and not blindly on each page load.

Also I heard somewhere that there could be some issues on IE on dynamic DOM script insertion, and that it would be preferable to use document.write() to add the script, I personally would really hate to use this solution, anyone knows something more about that?

Comment by giank — May 7, 2008

It is an interesting idea and like some others my concern would be performance. What happens when your ‘modularized’ code grows to be large. Besides having your javascript littered with a bunch of ‘If’ statements, many additional http requests are likely to slow down the loading of your application. I imagine once cached the performance wouldn’t be as bad. Anybody have any thoughts here?

I agree, there has to be a nice way of organising your javascript code but making it too granular has its problems. I prefer to manage it on the server with a few javascript files. If there is some redundancy so be it, in the end I think it makes my life easier and more importantly doesn’t negatively impact the user experience which is always paramount.

Comment by andrewzielinski — May 7, 2008

@andrewzielinski, if you have an array of classes to check for, you could then build up an array of js files to load, and load them all in one go using a serverside script which appends them all together.

Comment by kae — May 8, 2008

We started with a similar solution but this only works if there are no dependencies to other lazily loaded Javascripts.

To build a better solution have a look at the YUI Loader (http://developer.yahoo.com/yui/yuiloader/)

Comment by bort1002000 — May 8, 2008

giank: I think what shawn is trying to say is that it’s the same technique present in scriptaculous.js

Comment by icoloma — May 9, 2008

icoloma, I tought shawn was referring to the function Automatic JavaScript response evaluation of the Ajax.Response command in prototype.js 1.60 .

Comment by giank — May 9, 2008

another example for javascript loading
http://fullajax.ru/examples/index.html#:addscript

Comment by sirus — May 14, 2008

Leave a comment

You must be logged in to post a comment.