Lazy Loading Asyncronous Javascript
Like many of you might know, I’m working on a site called Kundo with a couple of friends. It’s kinda like a Swedish version of Getsatisfaction, which means we have a javascript snippet that people add to their site to get feedback functionality. Cut-and-paste instead of writing the code yourself. Simple.
The problem is, how do you load an external javascript with minimal impact on your customer’s sites? Here are my requirements:
- Small. I don’t want a big mess for them to include on their sites. 10-15 lines, tops.
- Stand-alone. The environment is unknown, so we can’t rely on any external dependencies, like javascript libraries.
- Cross-browser. I have no idea what browsers my customer’s customers have, so I can’t do anything modern or fancy that isn’t backwards compatible. I assume at least IE6 and up though.
- Asynchronous download. The download of my script should not block the download of any script on their sites.
- Lazy Loading. If my site is temporarily slow, I don’t want to block the onload event from triggering until after our site responds.
- Preserve events. Any events used should not override any events on the customer’s site. Minimal impact, like I said.
- Don’t pollute namespace. Global variables should be avoided, since they could conflict with existing javascript.
Note: I did not make all of this up myself. Lots of people did, I’m just writing it down for you. Thanks: Jonatan, Steven, Peter, and Linus.
Script tag
While being the stand-alone, cross-browser, and the shortest piece of code possible; it doesn’t download asynchronously and doesn’t lazy load. Fail.

Screenshot from Firebug’s net console: The script (set to load in 2 seconds) blocks the download of the big image (added after the above script tag, and used throughout this article as a test). Onload event (the red line) triggers after 2.46 seconds.
Async pattern (A script tag written with javascript)
Steve Souders, the web performance guru, has compiled a decision tree over different ways to achieve non-blocking downloads. Have a look at that graph.
Since we’re on a different domain, and only have one script (order doesn’t matter), the solution is given: We should create a script tag with inline javascript, and append it to the document. Voila! Non-blocking download!
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'http://yourdomain.com/script.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
})();
Note: async is a HTML5 attribute, doing exactly what we’re trying to simulate with our hack, so it’s added for good measure. Also, wrapping the code in an anonymous function prevents any variables to leak out to the rest of the document.
This is a pattern that is getting more and more popular nowadays, especially since Google Analytics uses it. But there’s an important distinction here: The above snipped blocks onload from triggering until the referenced script is fully loaded. Fail.
Update 2010-09-01: Steve Souders adds that the above is only true for Firefox, Chrome, and Safari, but not IE and Opera. So for a IE-only site, this might be the best method.

Screenshot from Firebug’s net console: The script (set to load in 2 seconds) downloads in parallell with the big image. Onload event (the red line) triggers after 2.02 seconds.
Lazy load pattern (Async pattern triggered onload)
So, how to you make sure you don’t block onload? Well, you wrap your code inside a function that’s called on load. When the onload event triggers, you know you haven’t blocked it.
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'http://yourdomain.com/script.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
This works, but it overrides the onload event of the site that uses the script. This could be OK in some cases, where you have control over the site referencing the script, but I need to cater for that. Fail.
Unobtrusive lazy load pattern
The logical solution to the above problem is to use an incarnation of addEvent. addEvent is simply a common name for an cross browser way to take the current function tied to onload, add it to a queue, add your function to the queue, and tie the queue to the onload event. So which version of addEvent should we use?
There’s been competitions for writing a short and compact version of addEvent, and the winner of that competition was John Resig, with this little beauty:
if (obj.attachEvent) {
obj['e'+type+fn] = fn;
obj[type+fn] = function(){obj['e'+type+fn](window.event);}
obj.attachEvent('on'+type, obj[type+fn]);
} else
obj.addEventListener(type, fn, false);
}
Note: This is unsafe code, since it relies on serializing a function to a string, something that Opera mobile browsers have disabled.
Thing is, we don’t need all that generic event stuff, we’re only dealing with onload here. So if we first replace the type attribute with hardcoded ‘load’, replace obj with ‘window’, and remove the fix for making ‘this’ work in IE, we’ve got four lines of code left. Let’s combine this with the above lazy load pattern:
function async_load(){
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'http://yourdomain.com/script.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
if (window.attachEvent)
window.attachEvent('onload', async_load);
else
window.addEventListener('load', async_load, false);
})();
This is exactly what we’re looking for here. Finally!

Screenshot from Firebug’s net console: The script (set to load in 2 seconds) downloads after the onload event has triggered. Onload event (the red line) triggers after 0.41 seconds.
And that wraps up this article: Different tactics works for different scenarios, and only understanding them all makes you capable of picking the right one for your problem. As always, I’m waiting for your feedback in the comments. Thanks for reading!
Dean Higginbotham ( 8 May 2010 )
What an awesome writeup! Really good info and I love the break-down comparisons and proofs.
Totally bookmarking this to give it a thorough read-through! I’ve been looking for this info for a long time and everywhere else it’s a mish-mash of “do this!”, “no, do this!”, “no, this is the right way!”, etc…
Morgan Cheng ( 8 May 2010 )
Nice article!
In my experience, since jQuery is used, most actions can be done at ‘DomContentLoaded’ event instead of ‘load’ event. So, it really doesn’t matter async loaded javscript delay ‘load’ event. Is there any specific requirement that the javascript should not delay ‘load’?
Emil Stenström ( 8 May 2010 )
Update: Switched to using insertBefore instead of appendChild in all script due to a IE6 bug with unclosed tags in the head. Thanks Peter Lindberg!
Emil Stenström ( 8 May 2010 )
@Dean: Glad you like it, that’s why I keep writing :)
@Morgan: I have no idea what techniques my customers are using, so I don’t know if they use jQuery, prototype or just native javascripts relying on the onloaded event. Would be great if I knew that all of them used a library that triggered their scripts on domcontentloaded, but I can’t know that for sure.
jarvklo ( 8 May 2010 )
Awesome.
Thanks for sharing!
Kristoffer Nolgren ( 8 May 2010 )
Jaha, är du med och bygger den, vi använder den på resihop,nu och är galet nöjda! (i alla fall jag, min optimeringshysteriska kollega hade gärna haft något som laddade snabbare och vill bygga nått själv, men designen och hastigheten är grym)
Emil Stenström ( 8 May 2010 )
@jarvklo: Thanks for reading!
@Kristoffer: Som du märker så jobbar vi hårt på att få upp hastigheten :) Det kommer mera framöver!
Kristoffer Nolgren ( 8 May 2010 )
Ja, den är ju grymt snabb redan nu!
En tanke jag haft, som ni kanske också har övervägt är att erbjuda en serverside-lösning, i alla fall för själva knappen, som ju är vad de allra flesta av användarna upplever.
Egentligen är det kanske inte så jättemycket som ni faktiskt behöver koda, bara att erkänna det som “good practice” att inte ladda knappen via javascriptet. Vi har valt att inte göra det för att undvika att nått pajjar vid uppdateringar och så.
Mathias ( 8 May 2010 )
Nice write-up!
Well, you could optimize this snippet even more if you wanted to. For example, why are you setting
? It’s completely unnecessary. You could also cache the string
and a store a reference to
in a variable, and re-use those to save even more bytes. (This is exactly what’s going on in my optimized asynchronous Google Analytics snippet.)
Also, if you don’t want to pollute the global namespace, you should probably prepend
with
.
The
check at the end can be rewritten as follows:
Emil Stenström ( 8 May 2010 )
@Mathias: That’s a great article, thanks! When I said short, I really meant “not too long”, which I think my final snippet fulfills. I think people wanting to optimize even more will use your hints to do it. Thanks!
Emil Stenström ( 8 May 2010 )
@Kristoffer: Customers are free to host the button on their own sites if they want to, but we have avoided this to keep the number of steps needed to get started with kundo to a minimum. We’ll see what happens in the future.
Roland Bouman ( 8 May 2010 )
Really, really useful info, and marvelously written. Kudos!
thanks for sharing :)
Roland.
Aki Kärkkäinen ( 9 May 2010 )
Good stuff! Just finished reading Steve Souders’ book Even Faster Web Sites so your article will come in handy. Nice to have weapons and tools in the current “browser speed war” ;) It seems that it’s all about fast loading time nowadays (which is good for users and everybody of course!).
Bryan ( 10 May 2010 )
When you declare
without the
keyword, does that not expose it to any external methods? I thought that you needed to use
to keep the scope local to that function.
Emil Stenström ( 10 May 2010 )
@Bryan: Of course, my fault. I’ve updated the code with a “var” in front of the x variable. Thanks!
Rizo ( 12 May 2010 )
Really good writeup Emil. Simple, yet great :)
Robin Jakobsson ( 13 May 2010 )
Great article Emil!
Valuable lessons,
thanks!
CodeMyConcept ( 24 May 2010 )
You did a great sum up of the regular issues. This article is being bookmarked right now!
Thank you!
Linus G Thiel ( 25 May 2010 )
Great solution, Emil. I will definitely be using this. Thanks for the linkback, even though I weren’t of much help!
Stephan Schubert ( 25 May 2010 )
Nice writeup how it works – here’s a jQuery version for the Google Analytics snippet: http://gist.github.com/361051
Josh ( 25 May 2010 )
With the final piece of code you might want to remove the ‘s.async = true;’ line? It seems a little irrelevant if the code is only added after onLoad.
Riyad Kalla ( 25 May 2010 )
Stenström,
Brilliant writeup. I didn’t expect you to walk through all past-and-present options for async loading… this is going in my JS quick-reference toolbox of bookmarks. Bravo dude.
Emil Stenström ( 26 May 2010 )
@Josh: Good question, and I thought about that too. Thing is, there could be more than one script triggering on onload, and I would hate this script to block the other ones. That’s the reason it’s still there.
Steve ( 7 Jun 2010 )
Very informative post.
I’ve never thought about loading external scripts much, I am just getting in to Javascript and this has helped me out lots.
Thanks :)
Ashish ( 11 Jun 2010 )
Hey,
It’s nice post, We have already tried this in few of our website projects and it works flawless.
Thanks :-)
Roi ( 14 Jun 2010 )
Great post.
Thank you!
onequad ( 20 Jun 2010 )
Thanks for sharing! I’ve looking for a way to do just this. I’ve tried loading http://tracker.stats.in.th/tracker.php?uid=18244 but it didn’t work. I am guessing that the external script has document.write and that messes things up. Is there a work around for this?
Steve Souders ( 31 Aug 2010 )
Great article! A few comments:
Under Script Tag you have script type=”", but I think you mean script src=”".
Async Pattern (dynamically added SCRIPT element) is rejected because it blocks the onload event. Although this is true in Firefox, Chrome, and Safari, it’s not true in IE and Opera. Also, I haven’t tested this recently with growing support for the SCRIPT ASYNC attribute (altho the HTML5 spec says onload should be blocked, so I’m not optimistic).
Certainly waiting until after the load event has fired avoids blocking the page, but if a widget’s content is valuable to the page it’s important to load earlier. Note that 5-15% of pages don’t reach the load event – users click through before that happens.
Emil Stenström ( 1 Sep 2010 )
@Steve Souders: Thanks for you comment. I’ve fixed the first issue, and added a clarification about blocking.
About onload: Yeah, it’s unfortunate to have to wait that long. As a supplier of an external service, we feel that the customers own site always have to be prioritized above our script. So blocking their onload event is not something that we think is acceptable.
I can only wish that all ad suppliers did that same.
Aaron Peters ( 1 Sep 2010 )
Great article!
Have you considered dynamically creating an iframe?
I know the Meebo guys went down this path and are very happy with the result, although it wasn’t easy to get this to work cross-browser.
http://blog.meebo.com/?p=2633
Gavin ( 2 Sep 2010 )
a great article, and great solution to a common problem.
the perfect way to include 3rd party code without affecting page load
Martin ( 7 Sep 2010 )
Nice job!
I’m using the Async pattern but I’d changed the closure statment by a setTimeout call. It’s working perfect on IE and Opera.
Enrico ( 9 Sep 2010 )
Nice article, helped me a lot.
But I encounter a problem with browser caching and the version with the onload event. The browser does then no longer recognize any changes in the loaded javascript.
I also opened a question at stackoverflow:
http://stackoverflow.com/questions/3674830/caching-problem-with-asynchronous-javascript-loading-with-onload-event
Maybe somebody knows a solution to this?
Emil Stenström ( 9 Sep 2010 )
@Enrico: Thanks! I replied to your question on StackOverflow, hope it helped.
Emil Stenström ( 9 Sep 2010 )
@Martin: Good idea. Does files loaded that way block other downloads?
Emil Stenström ( 9 Sep 2010 )
@Aaron: No, I haven’t, sounds lite an interesting article, thanks for the tip!
Alexander ( 18 Sep 2010 )
Thank you for such a great explanation! saved my time
Nicolas ( 18 Sep 2010 )
Thanks for this study, I love to read new articles about web performance :)
Damien P. ( 23 Sep 2010 )
I knew loading asyncronous javascript was difficult but i never thought someone could do it as easy as this. Thanks !
G ( 4 Nov 2010 )
Thanks for the great write-up Emil.
Can you elaborate a bit on what the best-practices are for the script that gets inserted (http://yourdomain.com/script.js in your example code).
What should be in there? I tried to look into your http://static.kundo.se/embed.js code but that is minified.
Thanks in advance!
Peter ( 9 Nov 2010 )
Wow, great article Emil! I am building a website which loads a lot of scripts (jQuery, Cycle, Hyphenator, Highslide, SWFObject, GA, etc) so I need good asynchronous loading!
I have one question. Is it possible to have fallback scripts for when a external script fails to load? For example, I would like to use Google’s version of jQuery since that will already be cached by many users, but I need a local fallback version in case Google is not available (Google is blocked in several countries). However, how do I check if an asynchronous scripts is properly loaded?
Thanks anyway for the great explanation!
Emil Stenström ( 9 Nov 2010 )
Hi Peter,
What most people do it first try to load the jQuery script, and then check if the global object “jQuery” is defined, otherwise load it another way. Here’s a thread from StackOverflow with lots of good answers: http://stackoverflow.com/questions/1014203/
DealsKing ( 24 Feb 2011 )
You should check out the optimized GA snippet from HTML5 Boilerplate, it’s even more minimal :)
Joakim ( 11 May 2010 )
Kan jag verkligen rekommendera att läsa igen!
This comment was originally posted onFriendFeed
Emil Stenström ( 13 May 2010 )
Tack för rekommendationen :)
This comment was originally posted onFriendFeed
Tim Akinbo ( 25 May 2010 )
How to properly load Javascript.
This comment was originally posted onFriendFeed
amix ( 25 May 2010 )
Another solution might be to set the sourcing of JavaScript in a timeout, e.g. setTimeout(_sourceJavascript, 300). I think this will bypass the blocking nature. This is only based on my intuition thought – it isn’t tested.–
It’s also important to note that window.onload will only fire when everything is loaded (including scripts and large images). To bypass this problem you should use something like jQuery.ready(document) or Dean Edwards onload hack ( http://dean.edwards.name/weblog/2005/09/busted/ ).
This comment was originally posted onHacker News
enomar ( 25 May 2010 )
IIRC, the setTimeout trick only works if window.onload executes before your timeout fires.
This comment was originally posted onHacker News
EmilStenstrom ( 26 May 2010 )
Most scripts need access to the DOM, and waiting for the onload or domcontentloaded events ensure that the DOM has properly loaded when you run your script. Using setTimeout does not ensure that, so you can’t really write a script tag then…
This comment was originally posted onHacker News