Dynamic loading of Javascript

When programming desktop applications I’ve always been a supporter of lazy loading of objects but I’ve never given this much thought when creating websites. Until now.

What happened you ask? Well,

one of my sites got dugg earlier this week. As most of you know, this means a lot of visitors in a short period of time, often referred to as the Slashdot/Digg Effect. I’m planning on blogging on this experience in a bit, so I’ll skip that part for now (come back in a couple of days if you’re interested). So, let’s move on to …

Why is this a good idea for (some) websites

I’ll use the page that got dugg as an example. It’s basically a page with a video from Google Video with some controls on the right side which can be used for adding comments or sending the video to a friend.

A couple of facts: – These controls are AJAX based, and I use the infamous prototype.js for this. – prototype.js is 47k – About 0,03% of the visitors write comments/send e-mail and prototype.js is only used when this is done – When I was dugg, I used 1,7GB in bandwidth only for prototype.js!

Starting to get the picture?

If some javascripts are only used by a small percentage of your visitors – why load them each time? It will only cost you bandwidth and your visitors download time. It took me digg to drain 1.7GB of my bandwidth unnecessary for me to get this ;) … Lazy loading is the key. Or dynamic loading of .js files if you will.

So, I started doing some research, and asked around in #javascript on ef-net. One user there, km0tion, linked me to this script which was exactly what I needed. Here’s the code:

/** include – including .js files from JS – [email protected] – 2005-02-09 ** ** Code licensed under Creative Commons Attribution-ShareAlike License ** ** http://creativecommons.org/licenses/by-sa/2.0/ **/ var hIncludes = null; function include(sURI) { if (document.getElementsByTagName) { if (!hIncludes) { hIncludes = {}; var cScripts = document.getElementsByTagName(“script”); for (var i=0,len=cScripts.length; i < len; i++) if (cScripts[i].src) hIncludes[cScripts[i].src] = true; } if (!hIncludes[sURI]) { var oNew = document.createElement(“script”); oNew.type = “text/javascript”; oNew.src = sURI; hIncludes[sURI]=true; document.getElementsByTagName(“head”)[0].appendChild(oNew); } } }

So the clue would be to only include include.js when loading the page, and then using it’s include() function to retrieve prototype.js and the other files. This is only done the first time a user uses the controls on your page that needs the AJAX libraries. Of course, it would be a good idea to show that familiar ajax loading image when doing this ;)

Can’t wait to implement this on 43min.com.

Also, I plan on analyzing the digg effect in a post the next days, and also letting you know how Codeigniter handled the traffic (43min is built on CI) – so if this is stuff you find interesting you might want to subscribe to my feed.

Comments

Comment by Richard Marr on 2006-11-01 17:36:29 +0000

This approach is used by libraries like Dojo (and I think Scriptaculous). It’s also useful for transfering data, eg;

http://p.moreover.com/page?query=javascript&#038;o=js001

As a side note, it’s funny how much of peoples’ programming history you can see from the code they write :o)

Comment by Nate Cavanaugh on 2006-11-01 22:40:36 +0000

Unfortunately Safari doesn’t allow you to append child elements to the head element.

The most consistent way to do this, believe it or not, is to do a document.write(‘< script >… < /script >’); which is kind of a brute force way to make sure it gets written all the time.

Just a heads up :)

Comment by Sean O on 2006-11-02 18:56:08 +0000

As a somewhat snarky aside, you may want to switch to a much less bloated JS library like JQuery to begin with! ;)

Comment by Bjørn on 2006-11-05 18:40:38 +0000

Thanks for the heads up :-)

JQuery looks good, I’ll definitively have to check it out.