Beware of getScript callbacks when document.write and eval get involved

Update, 10/11/11: Since I first wrote this post several excellent script loaders have been released, notably YepNope.js and my last post on document.write I’ve been trying to work out why I was having initialization problems with some lazy-loading functions I’d written. Document.write had been the main suspect, but there was something stranger going on.

JQuery’s getScript method

There are two key things about getScript to know, first is that the AJAX call to get the script is asynchronous, second is that the callback is fired on load*. These are both important points to consider when doing lazy loading. When called asynchronously there is no guarantee that the scripts will return in the same order they’re called. Also the callback is fired onload not when the script has been parsed and is available to use.

The situation

In my code I’d relied upon the callbacks to provide the initialization. All was going well until the third-party scripts were integrated and every so often the script wouldn’t initialize. Having looked in the ever useful Net tab in Firebug it happened when a certain pair of scripts were being initialized after DOM content loaded (dom ready) and before load, right when the lazy loading was running.

The problem, delay in parsing

The initialisation problem was caused by the disconnect between the onload call and the script completing parsing. The third party script is loading and doing a big eval on a string, tieing up the parser and exacerbating, but thankfully showing me, the problem.

Some kind of solution

The easiest solution is to have the script initialize itself, unfortunately I couldn’t do that as I needed three files, one a second library that the code was written in, second the data required by the function and third the actual code that makes it all work**.

What I decided to do was to raise a custom event reporting that the script had loaded and the name of the script. Then in my original script changed the event the initialization was called on to that event.

 
//trigger event 
jQuery('body').trigger('scriptLoaded',sScriptName); 

//event handlers 
//(mainLoaded is another custom event, fired one the primary 
//JavaScript interaction function have been called 
jQuery('html') 
.bind('mainLoaded', function(){ 
jQuery.getScript('lazymodule.js'); 
}) 
.bind('scriptLoaded', fScriptLoadedCallback); 

The future! It’s only going to be more of a problem…

Lazy-loading is going to be much more mainstream, the defer and async attributes have made it into the webkit builds so the problems described above will become easier to cause! Event driven programming can work around this problem and maintain the structure of your files.

*That the onload relates to the file and not the parsing of the code is true, but only as far as i know. No guarantees that’s consistent.

**Why three files? It’s going into a CMS and needs to be maintained, I could have lumped the data in with the code but that’s easier for people to break. I could have merged them all server side but the CMS guys have enough to do. And I could have rewritten the whole thing instead of using another library, but it’s 4500 lines of code that I’d be happy never to read again.

Filed under: JavaScript