Thursday, July 30th, 2009

Best way to load your JavaScript

Category: JavaScript, Performance

<>p>Nicholas Zakas thinks he has the best way to load JavaScript.

Steve Souders has a bunch of best practices, and it seems that there is definitely nuance that makes advice very much tailored to your circumstance.

Nicholas though, has an opinion:

I’ve come to the conclusion that there’s just one best practice for loading JavaScript without blocking:

  1. Create two JavaScript files. The first contains just the code necessary to load JavaScript dynamically, the second contains everything else that’s necessary for the initial level of interactivity on the page.
  2. Include the first JavaScript file with a <script> tag at the bottom of the page, just inside the </body>.
  3. Create a second <script> tag that calls the function to load the second JavaScript file and contains any additional initialization code.

A helper to make this happen could look like:

javascript
< view plain text >
  1. function loadScript(url, callback){
  2.  
  3.     var script = document.createElement("script")
  4.     script.type = "text/javascript";
  5.  
  6.     if (script.readyState){  //IE
  7.         script.onreadystatechange = function(){
  8.             if (script.readyState == "loaded" ||
  9.                     script.readyState == "complete"){
  10.                 script.onreadystatechange = null;
  11.                 callback();
  12.             }
  13.         };
  14.     } else {  //Others
  15.         script.onload = function(){
  16.             callback();
  17.         };
  18.     }
  19.  
  20.     script.src = url;
  21.     document.getElementsByTagName("head")[0].appendChild(script);
  22. }

In related news, the LABjs folk have updated their API from this:

javascript
< view plain text >
  1. $LAB
  2. .script("jquery.js")
  3. .block(function(){
  4.       $LAB
  5.       .script("jquery.ui.js")
  6.       .script("myplugin.jquery.js")
  7.       .block(function(){
  8.             $LAB.script("initpage.js");
  9.       });
  10. });

to the simpler:

javascript
< view plain text >
  1. $LAB
  2. .script("jquery.js")
  3. .block()
  4. .script("jquery.ui.js")
  5. .script("myplugin.jquery.js")
  6. .block()
  7. .script("initpage.js");

I seem to remember that Steve had some opinions on this API too :)

Related Content:

29 Comments »

Comments feed TrackBack URI

There is only one issue with this, if your using any of those external script calls on that HTML page, you will get tons of JS errors because the functions will not exist. This is great for a external scripts that are not used right away.

Comment by cfipilot — July 30, 2009

Not that anyone outside the BBC cares, but I don’t think script.onload works in Safari 2.

Jake.

Comment by jaffathecake — July 30, 2009

Of course there’s also the alternative in removing your business logic AWAY from your BROWSER and INTO the SERVER – where it belongs anyway – and thereby reducing the JavaScript size so significantly that “no-blocking JavaScript loaders” are basically rendered useless because the size of your JavaScript is so pathetically small that it doesn’t even matter anymore…

Comment by ThomasHansen — July 30, 2009

Loading scripts asynchronously (so they don’t block the page) can be complicated. It’s great to try and find a simple solution. Reducing complexity often results in reducing flexibilty (the “complexibility” curve). Nicholas’ technique requires that all your scripts be combined into a single script, and all your inlined code be wrapped inside a single callback function. If your site fits those constraints, this loading technique is good. If you need an alternative, I describe several other techniques in Chapter 5 of Even Faster Web Sites and in the examples from the book.

Definitely don’t load the initial script. It’s better to just inline the loadScript function, rather than put it in a separate file. For users with high latency or slow connections, a file this small could still take a second or more. Inlining such a small function avoids these delays.

A small fix for the code. Opera handles both onload and onreadystatechange for scripts, so the callback will get executed twice. It’s easy to workaround this by setting a flag so the callback is only called once, as shown in this example.

The LABjs code, last time I looked, wasn’t able to load multiple scripts in parallel if there were dependencies. You can see a way to do this across all browsers in this example.

Comment by souders — July 30, 2009

@ThomasHansen…I think you’re commenting on the wrong website. This is Ajaxian. Let me help you…this is not 1999 any more. Webpages are programs in themselves. They are no longer just a display for server-side data.

Comment by tercero12 — July 30, 2009

@ThomasHansen … Or… one could simply wisely and without bias choose the appropriate technology that matches the needs of the project as opposed to forcing an inappropriate technology solution onto a project that isn’t a match for it.

In any case… it’s an interesting approach. High latency and slow connections might be its undoing and the page needs to wait and verify any code it needs to use has fully loaded.

Comment by ChrisBAsh — July 30, 2009

This is a developer induced problem. Rather than looking for solutions to the problem, why not avoid the problem altogether, and place the JavaScript as the last element(s) on a page?

This also has the added benefit of promoting a clearer separation of content from behaviour.

Reiss

Comment by atwork8 — July 30, 2009

@ThomasHansen

Yeah, the whole reason why I was hires is because IT was tired of upgrading servers. Get with the times. :P

Comment by mdmadph — July 30, 2009

is best practice to dynamically append to ? any DOM/reflow events to consider?

Comment by kaiyzen — July 30, 2009

I develop a jQuery plugin that follow these guidelines and load css too:
http://plugins.jquery.com/project/includedemand

Hope everyone enjoy it. Load script on demand this way it’s very usefull, for jQuery mainly, you can load just the plugins that is used in that script.

Great article, this is a recurrent subject here…

Comment by alexanmtz — July 30, 2009

@tercero12 – and others …
I haven’t said anything against Ajax, in fact that would be pretty weird since I’ve exclusively worked with Ajax for many years now myself. What I do however propose, and what I feel especially tercero12 misunderstood is that we move the business logic away from the client and into the server. It’s still possible – in fact way easier to be honest – to create really rich Ajax functionality, running exclusively in the browser. Like for instance we’re demonstrating here; http://ra-ajax.org/samples/CRM-Sample.aspx – or here; http://ra-ajax.org/samples/Ajax-Forum-Starter-Kit.aspx
.
None of those samples doesn’t contains even *one* line of “Custom JavaScript”. Both of them are exclusively built purely on the server. Then they are being rendered back to the client automatically without the developer even needing to know that there’s any JavaScript at all existing to tie things together. In a way it’s a little bit similar to what GWT does (not really actually, but it’s the only way I can get people to understand it)
.
I realize that this concept, and my ideas, are old news for most seasoned visitors at Ajaxian, and I am sorry for repeating theme, but I felt a little bit of clarification was needed for some of the new commenters at this Ajaxian…

Comment by ThomasHansen — July 31, 2009

@ThomasHansen
First read and LEARN what business logic is, people use this term too loosely and tend to toss it up whenever some coding comes up that starts to look like real programming or code management.

“Technology” and “business logic” are NOT interchangable and business logic does not exclude client sided code management nor does it dictate that client sided code should be served through another language like java (GWT).

From what I’ve seen from GWT is that the ideal seperation of logic and presentation gets thrown out of the window and in fact, those kind of frameworks have a bigger risk of mixing business logic (which does indeed belong on the backend) into frontend logic. I suspect Ra-Ajax has the same risks.

Nay technology does not equal business logic and keeping business logic where it belongs is more a matter of knowledge and wits rather then what technologies you choose to use…

Comment by BenGerrissen — July 31, 2009

@ThomasHansen – what if you are designing web apps that need to support thousands of concurrent users at large companies? You could (1) put as much processing as possible on the servers, thus creating the need to spend more money on servers, or (2) put more processing on the client-side and make use existing client resources that would otherwise be unused.

Option 1 may be best in some situations, but Option 2 is commonly the best way in 2009, especially if clients are using modern browsers.

Comment by WillPeavy — July 31, 2009

@BenGerrison
I realize that you’ve got your opinion and that no matter what I say it is highly unlikely that I can make you change your mind, I do however feel the urge to answer you so that others reading what you just wrote don’t get the wrong impression…
.
Different Ajax Frameworks do to an extreme extend promote and “dictate” placement of code and functionality. And while it is (unfortunately) way too easy – all though not impossible to avoid I agree – to put business logic in the client layer (JS) with client side DHTML libraries like jQuery and such. This is way more difficult in GWT and in some frameworks like Ra-Ajax – virtually *impossible*. An example follows; Imagine you’ve got a “register new user form”. Then you need to verify that the username is not taken before submitting to the database for insertion. In jQuery et al I suspect many developers would create some JSON backend behaving kind of like a WebService or something which would return a boolean value indicating whether or not the username was already taken or not, and if not proceed with the submission towards the database insertion logic of the “insert new user” logic, if not for anything else then because of the simplicity of the code. Now the stuff that happens on the client in that scenario might have many names, be it technology, code, JS code or logic. But I suspect most seasoned developers will disagree with you if you choose to not define this as business logic. Sure the checking of whether or not that username is taken or not happens physically on the server, but how to proceed in the application workflow is 100% happening purely on the client.
.
There are many things that may go wrong here, first of all the application now has a way larger attack surface and you need to check another time just before inserting the new user into your database if the user exists or not and thereby duplicating code – some other dude might have taken that username in the meantime you know, hence DRY goes out the window, not to mention what do you do if the “checking” goes through while the “insertion” says that the name has been taken already? It might pass many seconds before the insertion, after the checking… Some other user might have in that window taken that same username…!
.
Or if you’re a little bit sloppy and trusts your JS a little bit too much and some user hacks your JS, modifying your variables after the checking but before the insertion…
.
Now seasoned jQuery developers will of course say that; “We don’t do that”. And thank you Lord for that! But most developers aren’t “seasoned”, and still if you do this operation in one “atomic operation” you’ll still need to have logic in the client layer as to what’s supposed to happen when the username is already taken…!
And this logic is what’s refered to as the “application workflow”, or Business Logic if you wish…!
.
In a purely server-side framework, like Ra-Ajax yes, this is impossible and none of those problems are even relevant since you’d be able to do the checking, insertion and potentially error reporting in one chunk of code, without any needs for doing anything in the client layer.
.
When that is said it is still possible in Managed Ajax Frameworks (like Ra) to combine Business Logic with UI code, which is sub-optimal and bad practice, but at least that “bad practice code” is on your server, where nobody can hack it, test its attack surface utilizing robots and JS snippets. And your “dirty laundry”, all though still “dirty” lives in a Blue Pill environment where nobody can mess with it and figure out “just how dirty it actually is”…
.
And the result is that your stuff is far more DRY, far easier to maintain, far more flexible to modify and extend and far more safe. And the reason is because you don’t have *Business Logic* in your client layer…!
.
Fat is that DHTML libraries reduces the server to nothing but a fancy relational database implementation with support for triggers and stored procedures, while a real Ajax Application Framework – yes like GWT and Ra-Ajax – will make it possible to make your server far more capable then just push data back and forth between your database and your JavaScript…!
.
You might refer to potatoes as tomatoes, or even (falsely) claim that I am the one doing just that, but that doesn’t change the facts…

Comment by ThomasHansen — July 31, 2009

@souders I am sure the callback won’t fire twice, read the Zakas’ code again.

Comment by kean — July 31, 2009

@ThomasHanson
You used the “business logic” card on the dynamic scriptloading technology/ideas of the OP. Out of place, futile nonsense argument.

Then you go into that DHTML libraries can create business logic where it doesn’t belong. Which is a whole other topic alltogether and does nothing to debunk the tech writeup of the OP.

Shamelessly promoting Ra-Ajax again in a unrelated topic?

Seriously, write a good blogpost about Ra-Ajax and send it in, I’m sure Ajaxian will post about it. The way you are going about it now only harms your cause as yer being a bleep for doing it like this.

As for your indepth points about business logic ending up where it shouldn’t, you’re right, it’s all true, except for the point where frameworks like Ra-Ajax are the best solution. Ra-Ajax is a choice and like GWT is a choice to make when you don’t have the right skillsets inhouse. If you got a crackerjack ninja frontend developer or two, GWT/Ra-Ajax won’t ever come close to the creative possibilities at your disposal.

Again, write a blog post on the terrors of business logic ending up where it shouldn’t and make a name the RIGHT way. Instead of riding on other peoples ajaxian posts.

Comment by BenGerrissen — July 31, 2009

@souders LABjs *will* load any scripts in parallel if the author specifies as such. It will similarly “block” if the author specifies that. Whether to do so or not is the author’s choice, and is based on them having a basic understanding of the dependencies involved in what they are adding to their page.

(And let’s face it, if you DON’T understand simple dependencies between scripts, you probably shouldn’t be trying to optimize your site like this anyway).

If a site has several scripts from different locations that are unrelated, they can load *all* of them in parallel. If they have one script that must load before 3 others load, they can do that do, by inserting one “block()” into the chain after the first script. It’s author’s choice on parallel loading vs. blocking, and IMHO that’s the best way, to let them organize loading order and behavior for most optimum for their conditions.

Consider:

$LAB
.script("script1.js")
.script("script2.js")
.script("script3.js")
.block(function(){
script1Func(); script2Func(); script3Func();
});

That chain loads all 3 scripts in parallel, and then executes whatever code you want afterwards.

You can also have, even in the same page, this kind of chain code:

$LAB
.script("script4.js")
.script("script5.js")
.block()
.script("script6.js")
.script("script7.js")
.block(function(){
script4Func(); script5Func(); script6Func(); script7Func();
})
.script("script8.js")
.block(function(){
script8Func();
});

In this chain, 4 and 5 load in parallel, then 6 and 7 load in parallel, then functions from 4-7 get executed, then 8 loads by itself, then finally a function in 8 is called.

The point is choice and flexibility, and balancing loading in parallel versus blocking where necessary. LABjs gives you just that, no more, no less.

Comment by shadedecho — August 1, 2009

Is it still only me that have managed to get errors when loading scripts using the readyState == “loaded” on IE 6. This state is set after the script has been downloaded but some times before it gets parsed and evaluated. I made some tests where I loaded large scripts and then hogged the CPU by making a loop in an external process. It would then some times fire the “loaded” event before the actual script evaluation and that would break script dependencies in other words it wouldn’t be blocking correctly. So I don’t think it’s possible to make reliable blocking script loading using that event you need a callback function in the actual script.

Comment by Spocke — August 2, 2009

I have an easier way:
defer=”defer”
on all your script tags

I know Firefox 3.5 finally supports it
http://bugzilla.mozilla.org/show_bug.cgi?id=28293

Not sure about webkit or opera but IE6,7,8 always had it.

Comment by ck2 — August 2, 2009

@spocke – the demo on LABjs is specifically aimed at loading 3 large scripts (~500kb each), and making sure to wait for all 3 to load before calling functions on them. I’ve extensively tested this demo in various browsers, including IE6.

I’d be very interested if you can get the demo to break. But as of now, I think this script is strong enough to work in all relevant browsers.

Comment by shadedecho — August 2, 2009

nice to see this, it’s something similar to a piece of work I did for moveme.com to simultaneously load all the javascript and css files required in a stream rather than waiting for the browser to render it step by step.

helped our end, reduced our loading times from 25sec to 6!

Comment by indiehead — August 3, 2009

@BenGerrison
Ref; “You used the “business logic” card on the dynamic scriptloading technology/ideas of the OP. Out of place, futile nonsense argument.”
.
Not really since when using a library which doesn’t create Business Logic on the client you often end up with only a fraction of the JS, and hence the whole “dynamic loading” argument becomes obsolete…

Comment by ThomasHansen — August 4, 2009

@ThomasHansen
Then you have no idea how rich RIA enteprise websites can really get ;)
For example, the following can be contained in a single website:

- Accordion (vertical, horizontal, different modes and extra options)
- Google Maps (adapters, and extra addons specific for the customer)
- Auto Suggest (different modes, complete or suggest)
- Flash placing, interaction.
- Popup/in window/modal mechanism
- Funky arse navigation system some designer came up with…
- Ajax Application Presentation layers (plural)
- Font-size script (gracefully degrading)
- Site trackers
- Unobtrusive GUI techniques
- Runtime Form Validation presentation layers
- Canvas (SVG) applications
- Text editors

And many more tidbits and applications a full blown enteprise website can harbor, all unobtrusive, gracefully degrading and ofc with business logic relayed to the backend.

Frontend presentation technology is becoming huge as clients demand more RIA and the audience becomes more spoiled. This all results in either a huge ammount of kb’s worth of javascript libraries loaded per page, a maintenance hell when including only the needed libraries per page or backend developers wasting their precious time working with an MVC or a frontend code compiler like GWT.

And even if you only have like under 100kb worth of javascript, dynamic loading still makes your pages load faster and can reduce server traffic (= savings on $) on very busy sites.

And I haven’t mentioned the fact you can let frontend developers and backend developers work side by side and independant of eachother instead of one team waiting for the other, finishing websites sooner and using the right people for the right jobs creating higher quality websites.

Little technology posts can make for huge innovative leaps and can inspire futher for example, XMLHTTPRequests is only a few lines of code when you get to the jest of it… I’m sure some people much like yourself dissed Ajax when it was in it’s infancy as well ;)

Comment by BenGerrissen — August 4, 2009

@ThomasHanson
You are an idiot!

Comment by MrGregor — August 4, 2009

@ThomasHanson
I dont know where you are! But if you are outside, I hope a big bird takes a shit in your face!

Comment by MrGregor — August 4, 2009

@ThomasHanson
…and i hope he does it asynchronously!

Comment by MrGregor — August 4, 2009

@BenGerrison
http://ra-ajax.org/samples/Viewport-Calendar-Starter-Kit.aspx
http://ra-ajax.org/samples/Ajax-Forum-Starter-Kit.aspx
http://ra-ajax.org/Docs.aspx
http://stacked.ra-ajax.org/
Profile those websites with e.g. YSlow and check out thsize of the JS…
.
And those websites are actually developed *WITHOUT* a so-called “front end developer”. They’re purely written in “server-land”…

Comment by ThomasHansen — August 6, 2009

@BenGerrison
Oh yeah, almost forgot those links are also developed with a framework which (yes) have Dynamic Loading automagically, but in practice it’s seldom used…

Comment by ThomasHansen — August 6, 2009

@ThomasHansen.
EitherYou’re going on a sidetrack again to shamelessly promote RaAjax OR you’re defending RaAjax whilst THAT is not being attacked.

I’m not arguing with you, I am putting you in your place since ALL of your ramblings have been offtopic and is generally considered rude.

Also, consider this, without people posting about snippets of javascript, RaAjax would not have existed AT ALL. You NEED those “so called frontend developers” else your framework grinds to a halt since what I see in RaAjax is NOT original, and NOT innovative, it’s just a rehash of something that has been done before with other languages and surfs on the ingenuity of frontend developers experimenting with javascript.

Comment by BenGerrissen — August 10, 2009

Leave a comment

You must be logged in to post a comment.