The best way to load external JavaScript

Not too long ago, I wrote about loading JavaScript without blocking by creating a dynamic <script> tag. When <script> tags are in the flow of an HTML document, the browser must stop rendering and wait for the script file to download and execute before continuing (example). Creating a new <script> tag via JavaScript avoids this issue because it’s out of the flow of the document, so the script file is downloaded and executed without waiting. The result: dynamically loading JavaScript files allows your page to render faster and therefore improve perceived performance.

The best technique

Steve Souders has explored several different ways to load JavaScript without blocking both on his blog and in his books. After thinking about it and experimenting, 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.

That’s it! There’s really no need to do anything else. The key takeaway is to have only two JavaScript and make the first one as small as possible. For example, the first file may just contain this function:

function loadScript(url, callback){

    var script = document.createElement("script")
    script.type = "text/javascript";

    if (script.readyState){  //IE
        script.onreadystatechange = function(){
            if (script.readyState == "loaded" ||
                    script.readyState == "complete"){
                script.onreadystatechange = null;
                callback();
            }
        };
    } else {  //Others
        script.onload = function(){
            callback();
        };
    }

    script.src = url;
    document.getElementsByTagName("head")[0].appendChild(script);
}

That’s a tiny amount of code to get your bootstrapped so it will load incredibly fast (especially when gzipped).

The actual code on your page ends up looking like this:

<script type="text/javascript" src="http://your.cdn.com/first.js"></script>
<script type="text/javascript">
loadScript("http://your.cdn.com/second.js", function(){
    //initialization code
});
</script>

The key to this whole technique is to have just two JavaScript files, so the second one contains everything that’s needed to initialize the page. What if your page requires more than two files? Then you should be concatenating your files together either at build time (using something like Sprockets) or at run time (using something like mod_concat or a combo handler). There should never be a time when your page requires more than these two JavaScript files to properly initialize. Each additional HTTP request has overhead, and then you’ll need to worry about sequencing the downloads so code is executed in the correct order. By having just two files, you eliminate a large point of concern over which file is downloaded and executed first as well as eliminating unnecessary HTTP requests.

Script placement

You’ll note that I mentioned the best practice of placing this code towards the end of the page, just inside the closing </body> tag. This is advice that has been around for a while and I still recommend it, even when using this technique. The reason is that you’re guaranteed all of the DOM elements you may need are already present on the page. Loading your scripts earlier could introduce timing issues where you would need to worry about using window.onload or some other method to determine when the DOM is ready to be used. By including this code at the bottom of the page, you are assured that the DOM is ready to be poked and you won’t need to delay initialization any further.

Inlining the first script

Several commenters correctly pointed out that this technique can be further optimized by moving the initial function inline instead of keeping it in an external file. Generally, I like to keep JavaScript outside of the page code for maintainability purposes. I also anticipated that the initial JavaScript code on the page will be larger than just this function for one reason or another. If you can have some sort of automation around injecting this into your page as an inline script, I’m all for it! The key point is to make sure the script is small enough that it’s runtime performance doesn’t affect page loading.

YUI 3 has you covered

YUI 3 is designed around this very premise. You can start by just loading the yui.js file and then use the built-in Loader component to dynamically load the rest of the YUI library. For example:

<script src="http://yui.yahooapis.com/3.0.0b1/build/yui/yui-min.js"
    type="text/javascript"></script>
<script type="text/javascript">
YUI().use("node", function(Y){
    //initialization code
});
</script>

This code loads in the YUI “seed” file first, then creates a new instance of the YUI object and indicates that the “node” component is necessary. Behind the scenes, YUI constructs a URL with all of the dependencies for “node”, dynamically loads it, then calls the callback function when complete. The cool thing about the YUI 3 approach is that you don’t need to worry about including the URL for the JavaScript statically, just indicate which components you need and the library figures out the correct URL to download (details).

Conclusion

Though there’s been a lot of research on ways to load JavaScript without blocking, there really is just one way that I’d recommend as a best practice. There should really be no need to load anything more than two scripts to get your site initialize and interactive. Make the initial JavaScript file as small as possible and then load in the larger one dynamically to avoid blocking. This is the simplest, easiest way to get all of your JavaScript onto the page without affecting the user experience.

Update (1-Aug-2009): Added section on script placement to clarify why I do it in <body> instead of <head>.

Comments

  1. Shimon

    Nicholas,
    Thank you very much for this input on dynamic script loading - it is very useful for me.

    Though i just wanted to make a small remark. Based on my personal experiments it is not the best practice to concat all files into one if you have different files loaded on different pages. You'd rather have two or three files which will be able to get cached than one reloaded every time you have additional script concatenated there.

  2. James

    jQuery also has you covered: http://docs.jquery.com/Ajax...

  3. pavithran

    Dude nice write up .

    But your CSS is messed up :P

    In this page for the code below YUI the text comes out of the border :D

  4. Kean Tan

    I would replace

    document.getElementsByTagName("head")[0].appendChild(script);

    with

    document.documentElement.insertBefore(script, document.documentElement.firstChild);
    // prepend instead of append to avoid KB917927

    for a more generic solution.

  5. Ara Pehlivanian

    Nice writeup. You've explained the technique really well. I think the only lingering question though is, how to include custom code in the YUI 3 model without writing a massive inline callback function.

  6. Isaac Z. Schlueter

    Kean's suggestion is a good one. "Operation Aborted" isn't an issue the way that you describe it, Nicholas, but it would be an issue if you called loadScript() in the head before it was completed.

    Have you considered the benefits of putting your loadScript function in the HTML directly instead of in a separate file? Also, by inserting the script at the start of the HEAD instead of the end, you can have the browser start downloading the JS right away.

    It seems like this might be better: http://gist.github.com/157681

  7. Christopher

    Great article.

    By the way: readyState is not just supported by IE but also by Opera.

  8. Felix Halim

    Nice post :), and I have two questions:
    1. If the first script is so small, is it better to just inline the script inside the page and put it at the top?
    2. If I have two frameworks to load, obviously the page is going to have more than 2 scripts, unless we first download the two framework and merge it manually into one big script. In this case the useful YUI 3's first script becomes useless?

  9. Tom

    1) The small loader JS file can be inlined in script tag on the HTML page. This eliminates one HTTP request.

    2) "There should really be no need to load anything more than two scripts to get your site initialize and interactive." Not necessarily true. It's possible it would be faster to have multiple JavaScript files rather than one, if they're sufficient large and loaded concurrently. Having multiple simultaneous requests can be faster. Most browsers allow up to 4 concurrent requests per domain.

  10. Mikael Ståldal

    If you strive for super fast loading, why not include the bootstrap code in the HTML page?

    Something like this:

    function loadScript(url, callback){
    var script = document.createElement("script")
    script.type = "text/javascript";

    if (script.readyState){ //IE
    script.onreadystatechange = function(){
    if (script.readyState == "loaded" ||
    script.readyState == "complete"){
    script.onreadystatechange = null;
    callback();
    }
    };
    } else { //Others
    script.onload = function(){
    callback();
    };
    }

    script.src = url;
    document.getElementsByTagName("head")[0].appendChild(script);
    }
    loadScript("http://your.cdn.com/onlyone.js", function(){
    //initialization code
    });

  11. Mark Boas

    Very interesting. I'm also looking at the best way to load JavaScript, specifically how to detect whether your CDN is down and to load an alternative local script instead.

  12. Alex Burciu

    Wouldn't inlining the first script avoid one blocking HTTP connection?
    Even though it's small, the extra HTTP connection could be expensive in terms of network latency and render time, especially for users with high RTT.

  13. Steve Souders

    Loading scripts dynamically can be a complex problem. As you note, concatenating all your scripts into one script is an awesome first step. However, there are many web sites where this isn't feasible. It's also important to address how embedded scripts are handled. With your approach, if there was an embedded script block that relied on symbols from second.js, it would cause undefined symbol errors in IE. It's not always possible to wrap all the embedded code within a single callback function. For websites that can do as you suggest (concatenate all scripts into a single script and wrap all inlined code within a single callback), the technique for loading the second script is good.

    As for the the first script, it's better to just inline the loadScript function, rather than put it in a separate file. First, if you did put it in a separate file you wouldn't want to gzip it - the cost of gzip and ungzip outweigh the benefits of compressing such a small file. But more importantly, 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 quick note on the code: your approach will cause the callback to get called twice in Opera. It's easy to workaround this by setting a flag so the callback is only called once, as shown in this example from my book.

    Be careful using YUI Loader - if you have multiple files, YUI Loader intentionally loads them sequentially. It's better to find a way to load them in parallel.

  14. Kei

    How about defining the defer attribute on the script element?

  15. Adam

    Best resource on the subject I've found - multiple implementations with different JS frameworks:

    http://stackoverflow.com/qu...

  16. Ralph

    Wouldn't the absolutely fastest method be to include, in the head of your HTML file, the entire text of the javascript file which contains everything to initialize the page?

  17. Josh

    What's the right thing to do for external javascript? For example google maps api. I was curious if you might get better caching with separate files that the browser had seen before but I don't understand browser loading well enough.

  18. Jesse Hallett

    Since the code for the loadScript function is so small, might there be an advantage to putting that code inline? That would cut out another HTTP request.

  19. Nagaraj Hubli

    why do we even need the first js file that contains a function which loads the js dynamically. can't we keep everything in one script block.

    just one script block.. which contains

    1. loadscript function, and
    2. calling the loadscript function to load the second js

    will this affect in anyway?

  20. wookiehangover

    Does this really represent a huge performance gain over minifying and batching your javascript?

    Typically, jquery, a couple of plugins, and a pretty hefty js file will minify down to around 30kb or so, and will on be one call to the server, rather than two.

    Nevertheless this is a great suggestion, I'm going to have to test this out to see if it can really make a difference!

  21. mike

    Why create first.js at all? Just include that function inline in your HTML.

  22. Rakesh Pai

    So, consider a site which uses a JS library. Are you suggesting that you concatenate the library code with the site's code to form one file and forgo all benefits you'd get by using say Google's CDN for loading the library? Or do you have a callback for the onload of the JS library which has loaded from the CDN to then load your own file, thereby making the download serial in any case?

    In fact, if it's just one file, what benefit will you get by this technique over say just loading the file using the script tag? After all, it's at the bottom of the page anyway, so most resources have downloaded already and this bootstrap then doesn't give any benefit.

    What you really need is not to make one huge file, but to load multiple JS files without blocking each other, but while preserving script execution order. In such a case, you should give a look at a script I wrote some time ago that achieves this.

    That said, I must say I liked how you've added the callback handling. I must see if I can include that in my code as well.

  23. Koen

    Do you know if JQuery and Dojo offer simular functionality?

  24. anonymous

    Nice work! In general, I've found that this technique is far more reliable and predictable than others, and your post cleanly illustrates how to put it into practice. One point of contention, however, is your claim that:

    >>There should never be a time when your page requires more than these two JavaScript files to properly initialize

    In practice, this is completely unrealistic for large commercial sites. Even with a build process in place, the issues surrounding code reuse and scalability that arise from trying to bundle all JS into just two files would never be justified.

  25. Will Peavy

    Why put the one that contains everything necessary for initial interactivity second?

    "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."

  26. Bill

    Why not just

    script.onload = callback;

    instead of

    script.onload = function () {
    callback();
    };

  27. John Sawers

    I have to wonder about concatenation though. If prototype.js is required by every page on my site, then it's going to be combined with a.js, b.js, c.js on pages A/B/C. That means the user is downloading prorotype+a.js on page A, prorotype+b.js on page B, etc. That a lot of duplication. You can mitigate some of that with aggressive caching, but if I change a.js, it will be re-concatenated with prototype.js and the user will have to download 90% the same exact code again on page A.

    Is that really a more efficient/performant solution if you take into account primed and empty cache users as well as the fact that a.js/b.js/c.js may be changing on a weekly basis ?

  28. Nicholas C. Zakas

    First off, I'd like to apologize for not being able to keep up with all the comments on this post. Full time jobs really get in the way of personal blogging! :)

    To answer a few of the common questions people have been asking:

    Yes, it would absolutely be better to inline the initial function to eliminate another HTTP request. In practice, though, you want to have one canonical source for this code, which typically means a separate file rather than copying this into every page of your site. If you can do that without requiring another HTTP request, that's even better!

    @Kean/Isaac - This code was intended to be called from within the body, which would avoid any operation aborted errors. I also opt for loading the JavaScript at the bottom of body because then I'm guaranteed that all of the DOM references I'll be using are on the page. Beginning the download in the head means I'll still need to worry about tracking when various DOM elements are ready to be used.

    @Rakesh - If you loading the JavaScript file using a regular script tag, then the code must be completely downloaded and executed before the page becomes usable. Having multiple HTTP requests only increases this amount of time. Dynamically inserting the JavaScript file makes it a non-blocking request so the page finishes rendering and initialization much faster.

    @John - The case you're talking about is the empty-cache experience. There's always going to be a hit for an empty cache, regardless of what you do. The real question is how to minimize it. I think your case, of downloading a common library on each page, is a good exception to this rule (especially if that library is hosted elsewhere such as on the Google or Yahoo! CDNs). In that case, you could potentially be better off with loading the loadScript() method first, then the library, then the rest of your code.

  29. Kyle Simpson

    I think that the advice to concatenate all JS into a single file is not optimal advice across the board. As with anything, there is no one-rule-for-them-all, because it depends on lots of things. And it's a balance game that must be carefully weighed.

    There are a number of reasons why some small, modest amount of additional JS files (and requests) can be better overall throughput than one single mammoth file:

    1. Browser caching. If you have a single file, and even one character of one "file" within that big file changes, users need to re-download the whole thing. Breaking it up into smaller parts can help reduce some of this risk.

    2. Parallelization: taking advantage of the browser's ability to bring in mutliple (2, 4, or even 8) streams of data at the same time allows the overall size of the javascript to load quicker in throughput.

    3. Common files cached from CDN's: library code, even popular plugins, hosted on CDN's, it's much better to load those from those locations (for hosting speed and for cache performance) than to host yourself and concatenate in.

    For me, I think a site with 4-5 JS files is about right. Too few, and I've swung the balance poorly on the above topics, more than that and I've probably just created too much request/response overhead.

  30. Nicholas C. Zakas

    @Steve - That's incorrect. This script will not call the callback twice in Opera. You'll note that I'm removing the onreadystatechange event handler after it is called once. This effectively avoids the double-callback issue that you mention. :)

  31. Kyle Simpson

    With respect to HOW to load the file or files you are going to load... I agree in principle with the approach presented here. But, in putting it into practice on my sites, I ran into real-world issues that led me to write the LABjs tool with additional intelligence/functionality added in.

    Namely, in practice, I see that there are often in fact a few JS files on a site that need to get loaded in parallel, usually from different sources (CDN's, etc), but that often there's at least one dependency in the "chain", which is that one file (like jquery.js for instance) must load before the other scripts (plugins, for instance) should load.

    So, in addition to loading things in parallel, I also think solutions for this problem need to provide effectively manageable "blocking" where necessary. And that is the goal of LABjs (Loading And Blocking JavaScript).

    Put simply, you can create one or more independent "chains" (inspired by jquery syntax) of script loading requests, and intersperse in the chain block() calls when you need to cause the loading to wait before continuing with the chain. All scripts that are defined as siblings in the chain, before or after a block() call, load in parallel, but scripts after a block() call don't load until the scripts before it have loaded.

    So, you can easily get parallelism in loading where needed, and yet still easily get blocking where needed, too. You can also cause executable code to be inserted into the block() of the chain, so that it will execute in order as well (like initializing a loaded library).

    Example:

    $LAB
    .script("script1.js") // will load by itself, but in parallel with rest of page
    .block()
    .script("script2.js") // will wait until script1 loads before loading.. but will aslo load in parallel with script3.
    .script("script3.js") // will also load in parallel with script2
    .block()
    .script("script4.js") // won't load until scripts 1-3 are loaded.
    .block(function(){
    script4Func() // do something now that script 4 (and 1-3 consequently) are all loaded.
    });

    Of course, there's flexibility in how you use it; you can define multiple independent chains of $LAB calls, you can pass more than one script to the script() function at a time, you can control whether scripts add to HEAD or to BODY, etc.

    -----------------

    But again, in all of this, the point of this library is to allow you to effectively manage the times when you need to load more than one JS file, in parallel, but also have one or more script dependencies that you want to block on to prevent race conditions. If you are in that boat, LABjs is a great solution for you. And it's <2.5k compressed, so quite a bit of functionality in a small package.

  32. Nicholas C. Zakas

    @Kyle - I agree that there is no absolute solution for any problem, but I must disagree that 4-5 JavaScript files is optimal. Parallelization only helps the transfer time, not the execution time, and you're still making blocking requests. At most, you'd want to load two external JavaScript files: one for the core code that isn't likely to change, and one for the app-specific code that is more likely to change. When loading in this way, the download is non-blocking so there's no perceived additional download time by the user. The more JavaScript files you have to load, the worst off the empty cache experience is. If that experience is too slow, you may have already lost your users so the full cache experience is less important. Also, if your site is created using progressive enhancement, then the site is still fully usable while the JavaScript code is downloading, so there's not a huge downside to going back to the server for more functionality.

    And though I appreciate your plugging of your tool (and think it's a fine piece of code, by the way), I think it fundamentally complicates what should otherwise be a pretty straightforward and simple process. When you need to put too much thought and planning into how you're loading your JavaScript and when, that indicates an architectural flaw in the system. We don't need a tank to solve this problem, we need a bicycle. :)

  33. Kyle Simpson

    @Nicholas - The reason 4 or so files is a more realistic scenario is because of the prevalence of CDN hosted files. For instance, you do a jquery site, and you're almost certainly loading jquery.js AND jquery-ui.js from a CDN. That's 2, before you even start. Then there's one or more plugins, and init code, which you may concat into one file, and host yourself. That's 3 scripts. Then there's analytics tracking, like ga.js. You definitely want to use google's hosting of that rather than self hosting, so you don't concat it either. That's 4. Where's the fifth one? I often times load in a script for social bookmark sharing, like addthis.js.

    The point is, I think self-hosting all those files is sub-optimal, even with the reduction in HTTP request overhead, because of the loss of cache performance (even a little bit is better than none), the speed of hosting (my server's are nowhere as good as a commercial CDN), and the loss of ability to make sure that a script with bug fixes gets updated ASAP.

    If you admit this is all a balance game and an art more than a science, then I think it's obvious that some of these arguments must be factored in. One blanket statement, regardless of what it is, is just plain wrong, IMHO, since no one answer works everywhere.

  34. Nicholas C. Zakas

    @Kyle - I'm always the first to admit that no one approach solves the problem 100% of the time and you'll note that I said extensively in this post that what I'm talking about is optimizing the initialization of the page. I never said that you should only load two JavaScript files on the entire page for its lifetime, I said you shouldn't need more than two to get your page up and running. I believe that is a true statement regardless of the site. Your examples of ga.js and addthis.js are examples of files that are not necessarily to get your page interactive (ads also tend to make subsequent requests, but we certainly can't stop them!). Once you have the page initialized, you're then free to bring in additional files behind the scenes without affecting page load experience. With the jQuery example, I'd theorize that you need jquery.js to initialize the page and then jquery-ui.js can be brought in after page load. There are any number of ways to slice it up, but my main concern is getting the page interactive and usable as quickly as possible.

  35. Kyle Simpson

    Sure, it's an admirable goal to bring in fewer JS files before you initialize your page. Less files (or smaller files) will result in quicker load time, which everyone agrees is a good thing. Except of course there's a balance even there in terms of the user experience -- if you bring in an ugly, unusable page really quickly, are you users really more grateful for that "FOUC" ux. But nevertheless, this is overall a good direction to pursue.

    Really, I agree with you in principle and I think what you're saying still fits well with my approach, because a good author would use either your technique, or Souders', or LABjs, or any other, and load only the bare minimum before the page init code ran, and then go on to loading non-essentials. But, I think your argument focuses too heavily only on the first phase of page initialization and stops short of covering the entire issue.

    You seem to be saying that loading parallelism and blocking are moot (or overly complicated) with the LABjs approach because all that really matters is getting the first 1 or 2 scripts in place, and that this can be done with smaller, simpler code like you've presented. And I would agree. But, while true, I think it ignores the rest of the equation.

    After you have the first phase 1-2 files load for initialization, there's still several others, as you've agreed, that need to load, and load as quickly and efficiently as possible, for the page to get its full behavior and charm.

    I believe a good solution will make both phases/tasks easy for web authors to deal with. Of course it will primarily have made the first 1-2 init files loading easier and better, as you've well argued is agreeably the most important to get right.

    But also, because that second phase's performance is still very important, I think a viable solution will also address optimization of how it loads the subsequent files. Parallelism is obviously still important, and there may still be some dependency blocking needs in real world scenarios.

    I just happen to think a solution is best suited for the masses if it can operate the same efficient way for both phases of page loading, so that web authors don't have too much complication to deal with. If the optimization entry barrier is too high (having to employ several different techniques just to load/init a regular web page), the masses simply may ignore all together our pleas to improve performance and just keep on using regular old script tags. And neither of us wants that! :)

  36. Alex Burciu

    Would it be possible for the callback to be invoked before the loaded script finishes executing?

  37. north

    Hi Nicolas, thanks for this article! I played around with this technique a bit, and for some reason IE (at least 6 and 7) throws an unknown error (or "object expected" - but I think that only happened if the first script didn't get loaded and a function depending on it was called) if I use a relative path to my js file. If I use an absolute one, it works fine. I don't get why. Anyone? Thanks

  38. Kean Tan

    @Nicholas Zakas I used document.documentElement because I firmly believe not all SGML docs have the head element.

  39. Nicholas C. Zakas

    @Kean - that may be true, but I can't think of a reason why an HTML document wouldn't. And since we're dealing strictly with HTML here, the script node is supposed to be either in the head or the body. I opted for head since I'm recommending using this script at the end of the body.

  40. Brett Brewer

    I've been trying the google code Minify library and it seems to solve most of these problems simply by letting you throw a list of files at it and it minifies and concatenates and caches them. It's a php-based solution. I'm wishing it was an easier task to integrate the official release with the Kohana framework so I could take advantage of memcached for the caching, but otherwise I really like it so far. It doesn't break jquery plugins like some other minify implementations I've tried. You can even configure it to pull in 3rd party scripts for google analytics, etc. though I haven't tried that yet.

  41. Marcus Westin

    Good stuff.

    This is the best way to load js with little setup cost. There's another interesting case that's more and more common, that requires another soluion:

    if you include a script tag from another third party service (say Meebo community IM or Apture) that resides on a server which you don't control, then that initial seed file can still lock up your page load.

    The best way to deal with this scenario is to include an iframe to a document on your domain, say loader.html, which in turn loads the javascript. This is clearly a bit of a hassle (you have to create loader.html as well as make all the js in there manipulate the DOM in window.parent rather than window) but the end user experience is the best possible.

    Cheers

  42. Kyle Simpson

    @Marcus -- that's an awful lot of trouble, and wasteful on memory. Why not use the above technique, or LABjs, to load that other file asynchronously, *after* you've already loaded the main init files for your page?

    Both solutions will load that file in parallel with the rest of the page, meaning the rest of the page can load quickly and move on, and this other long-poll file will just come whenever it comes. And both will also let you define a callback function to wait to be executed until it does arrive, if necessary.

  43. Kyle Simpson

    I have noticed an important behavior people should be aware of with these techniques. IE will fire the "loaded" event as described above, even if the script returns as a 404, 500, etc. Other browsers will fire it on 500, but not on 404. And some won't fire it on anything other than success.

    So, there's still no guarantees with this technique that your code gets there. You still need to write robust programming, with try/catch blocks, etc, or you need to accept that if your file fails to load, the user will get errors.

    @north My guess is that you're relative vs. absolute pathing is causing an issue where it loads one way, and 404's the other, and yet both times the handler fires, so you're probably seeing your error because of that.

  44. Nicholas C. Zakas

    Thanks for the added info, Kyle. Hopefully, your code is robust enough to recover from such errors. I'd venture to say that nothing should be out in production that could possibly return a 404 for a script...that means you did something very wrong. Or your entire server disappeared. The former is probably more likely.

  45. Johann

    I took the above code and made it a bit smaller: function load(u,c){c=c||eval;var b=document,s=b.createElement('script'),b=b.body,x='onreadystatechange',y='onload',z='readyState';s[y]=s[x]=function(){if(!s[z]||/de|m/.test(s[z])){c();s[y]=s[x]=null;b.removeChild(s)}};s.src=u;b.appendChild(s)}, 243 <abbr title="Bytes">B</abbr>.

    You can see it live on my QR Code Generator where it is included on the page.

  46. Johann

    Looks like I forgot about dot notation. Not anymore!

    function load(u,c){c=c||eval;var b=document,s=b.createElement('script'),b=b.body,x='onreadystatechange',y='onload',z='readyState';s.y=s.x=function(){if(!s.z||/de|m/.test(s.z)){c();s.y=s.x=null;b.removeChild(s)}};s.src=u;b.appendChild(s)}, 237 <abbr title="Bytes">B</abbr>.

  47. Johann

    Disregard that, the last version doesn't work :-(

  48. Bill

    I wasn't thinking when I made my comment about script.onload = callback;. Don't do this. This will cause the callback function to be passed the event object and the this keyword to reference the script element, but this will only be the case in browsers that don't support script.onload.

    To make it the same for all browsers, it should be wrapped in an anonymous function like originally posted. So, disregard my original comment.

  49. Bill

    Oops, I meant "browsers that don't support script.readyState".

  50. Ariel Balter

    I wonder if you can give an example that does not use YUI. I would like to be able to call jsMath from an external server. jsMath works by including the following line:

    load.js then sets up the document to call other routines as necessary. What would the syntax be to use jsMath in the way you discuss? Thanks, Ariel

  51. Ariel Balter

    My last post didn't render the line of javascript of course (Duh)! Here it is with [code tags and comments

    <!---->

  52. treblam

    According to Sounders' post,javascript only block the page in two ways:
    * Resources in the page are blocked from downloading if they are below the script.
    * Elements are blocked from rendering if they are below the script.
    But you've already put the big script file at the bottom of the page,there is nothing below the script,so I think there is nothing to be blocked,what is the reason to load it with the dynamic tag?

  53. Nicholas C. Zakas

    @treblam - In addition to what Steve mentioned, JavaScript download and execution blocks all user interactions on the page. By loading dynamically, you make sure the JavaScript download (often the longest part) doesn't block user interaction.

  54. Vincent Voyer

    The drawback here is you can't use the defer keyword on both script tags because IE can't always handle well defer on both inline scripts and external scripts.

    What about loading the second external script with adding lines at end of first.js then adding defer to external script tag first.js ?

    Inline script is never a good solution ...

  55. Dirk

    This solution doesn't appear to be working in FF 3.6.7. Scripts appended to the DOM still block page load.

    Can anyone confirm?

  56. Dirk

    To answer my own question, in the example first.js, right after script.type, set:

    script.async = true;

    Voila! Works in the most recent versions of FF (tested in 3.6.7 and 3.6.8) and doesn't change the behavior in other browsers.

  57. Henri Sivonen
    This solution doesn’t appear to be working in FF 3.6.7. Scripts appended to the DOM still block page load.

    Can anyone confirm?

    I can confirm by having looked at the Gecko code.

    A change has landed to mozilla-central that makes the advice given in this article work as advertised in Gecko. The change is on track to being in Firefox 4.0 beta 7, but it has already been backed out twice, so I can't promise it won't be backed out again.

  58. Miroslav Nikolov

    Interesting.
    I am working on my own library based on a small core for dynamically loading the rest of the functionality (from different files).

    In the case I load the files this way - checking if the file is loaded - it won't work.
    The problem is here:
    script.onreadystatechange = function(){
    if (script.readyState == "loaded" ||
    script.readyState == "complete"){
    script.onreadystatechange = null;
    callback();
    }
    };

    It seems there is no way to be sure if the file is loaded in IE6,7,8. callback may execute before the file is loaded, which may be not so big problem as we don't have any dependent files to load after the first one. But what if we have 2 or more dependent on each other files? In that case we need to be sure that the first is loaded in order to load the second, etc...

    I fixed that by putting small marker method at the end of each file. This is not very good decision, but it seems there is now way to predict when the files have finished their loading in IE. So the only one solution (at least for me) to check if the file is loaded for now is to put some markers in the files. This will work in all browsers, but as I said it has some drawbacks.

    What do you think, is there any better way to do that, that works consistent way?

    @moubi

Understanding JavaScript Promises E-book Cover

Demystify JavaScript promises with the e-book that explains not just concepts, but also real-world uses of promises.

Download the Free E-book!

The community edition of Understanding JavaScript Promises is a free download that arrives in minutes.