AlexSexton.com $('human#alex thoughts').filter(':not(.bs)');

8Jan/1020

Don’t let document ready slow you down.

I just wanted to quickly post about a common performance hit that I see in pages (including a bit of my old stuff), especially ones that load data on page load.

A common pattern for application development loads a page template with the application code included and then makes an asynchronous data request to load all the relevant data.

One example might be something like:

<html>
  <head>
    <script type="text/javascript" src="jquery.js" />
    <script type="text/javascript">
      $(document).ready(function(){
        // make an ajax request once the dom is ready
        $.ajax({
          url: 'ajax.php',
            success: function(data) {
              for(var i in data) {
                $('.app-container').append('<div>'+data[i]+'</div>');
              }
            }
        });
      });
  </script>
</head>
<body class="app-container">
  // Perhaps a very large dom - which makes the page load slower
</body>
</html>

The problem with this is that the request for the page content isn't made until the dom is ready, but that request doesn't require the dom at all.

Instead of wrapping the $.ajax() call in a dom ready function, try just wrapping your `success` callback function in one instead. This will allow the request to fire off as soon as possible, but will ensure the handling of the data doesn't happen until your domready event has fired.

<html>
  <head>
    <script type="text/javascript" src="jquery.js" />
    <script type="text/javascript">
      // make an ajax request right away
      $.ajax({
        url: 'ajax.php',
          success: function(data) {
            $(document).ready(function(){  //<-- Hey Guys check this out!
              for(var i in data) {
                $('.app-container').append('<div>'+data[i]+'</div>');
              };
            });
          }
        });
  </script>
</head>
<body class="app-container">
  // Perhaps a very large dom - which makes the page load slower
</body>
</html>

This should speed up the load time of your page. From my testing, there doesn't seem to be any issues with this, so I believe it's safe to use. The function will fire immediately if your domready event has already fired, and it will wait to fire if the dom isn't ready yet. You can have as many document ready calls as you want on your page without overwriting old ones, so don't worry about only defining it in one place. I would also imagine that you wouldn't need too many more than two or three if you structured your code in an intelligent way.

---

As a footnote, this is only one of many things you can do outside of dom ready to help speed up the execution of your code. If you are binding events with 'live' or if you are just defining functions, I'd suggest that you do it inside of a closure instead of inside of a dom ready function. Then put just the stuff you need to run with the dom into the dom ready function.

The reason for this is essentially the same as before. You can allocate all the function definitions and variables while the page is building, rather than afterwards. Anything that you can do asynchronously right away is going to generally be better than waiting until the document is ready. There are a few ways to make this a little better organized, but organization isn't the topic, so here is a simple example of code you might write with minimal code execution in the dom ready function. The closure that I have surrounding the entire block of code serves to make the variables have a similar scope affect as it would inside of a document ready function, though it's not absolutely necessary.

(function($){  //<-- use a closure to protect your namespace and your `$`
  // This runs immediately
  $('a').live('click', function(e){
    alert('OKBYE!');
    return true;
  });

 // define functions outside of the dom ready
 function doSomething(input) {
    $('div.something').do(input);
    // you can even use functions that require the
    // dom inside of a defined function as long
    // as you don't call it until the dom is ready
    $('div.somethingelse').click(function(){
      console.log("I don't need no dom ready");
     });
  }

  // dom ready
  $(document).ready(function(){
    // Do stuff that requires the dom, and
    // use your functions from outside
    $('a.special').click(function(e){
      doSomething('special');
    });
    // yay!
  });
})(jQuery);

It's nothing revolutionary, but I figured I'd get it out there!

Comments (20) Trackbacks (1)
  1. I’ve actually been doing a lot of ajax requests on my pages at work and this makes so much sense and is going to increase their performance SO much. thanks for this.

  2. Brilliant idea, Alex! I hadn’t thought of that. Definitely going to come in handy.

    • To clarify (so I don’t sound like such a noob), I hadn’t thought of doing this with $.ajax. Have been doing it with .live(), etc.

      Great article. You should write more often. :)

      • If anyone thinks that “not thinking of a way to speed up a really edge-case webapp a couple of milliseconds” makes you a noob, then they are crazy. Thank you for the feedback! I really appreciate you taking the time to read my stuff.

  3. Doing your live() bindings outside of document ready() is really wise. If the code is in the (bad!), we’d get an early exit from Sizzle since the DOM isn’t available. I wonder how early the bail is.

    Smartnses.

  4. Nice work Alex. An upvote for you on codebix.com from me.

  5. ahaha simple & great tip. Thanks!

  6. I am loving the idea to put .live() outside of DOM ready – that’s genius. Everything else in the list I (try to!) do – but moving live calls outside too really is thinking outside of the box (sorry to have used such a fluffy saying there).

    Simple stuff with a lot of impact – the best kind of stuff :)

  7. Very nice idea and good article. I like the idea of .live. A small note you have a syntax error in console.log(‘I don’t need no dom ready’); it should be console.log(“I don’t need no dom ready”); or console.log(‘I don\’t need no dom ready’);

    Thanks

  8. @Hazem Thanks for the catch, ‘I\’ll update that right now.’ ;)

  9. Great and very simple!!! Thanks a lot!

  10. I have run into issues with this technique due to the browser limitation on HTTP requests to the same domain. If you perform AJAX requests while the page is loading, then those requests could potentially block other things (like images, etc.) from loading until the request finishes.

    In some circumstances (especially with unreliable services that might not respond in a reasonable time), this can lead to a slower experience (either real or perceived).

    In the end, it depends on the purpose of the code – if it’s the primary purpose of the page, they try to get it working on the page as soon as possible. If it’s secondary, let the page initialize as soon as possible, then run your code on document ready (or even document load if it’s really not that important).

    • I agree that it very much depends on the purpose of your ajax request.

      In this case, all the relevant data on my page comes in through that request. The dom is essentially a set of bare templates to be filled in by my application. This would be common practice in many apps that use ajax to load subsequent pages and uses hash urls, etc.

      That’s why you’ll also notice, that for this implementation, I have jquery loaded in the head. Generally this is against performance tips, but if the goal is to see the content as soon as possible, it makes a lot of sense here. In my _actual_ application that inspired this post, it only had to work in webkit, so I actually made a native XHR request (since I didn’t have to worry about crossbrowser stuff) for the first request, and still loaded jquery down at the bottom.

      As far as single domain max requests, I think it would be pretty simple for anyone with a need for added performance to set up a separate subdomain or two. You can just mirror the whole site on the subdomains, and you won’t have to worry about older browsers limiting you to two requests. (In the case of the _actual_ app, I pulled all my static resources from a cdn, which has it’s own added benefits on top of not blocking my ajax requests).

      I think, regardless of when you make that ajax request, if you are worried about max server connections, you should add in subdomains, or a cdn. Chances are, the dom will be ready long before all your requests are finished, which would block the ajax call until they opened up.

      Thanks for the salient input!

  11. jQuery tip of the year (since Jan 2009 :))! Thanks a lot.

  12. I agree doing the live/livequery calls outside document ready generally makes a lot of sense. But, personally I feel it really depends on the situation for ajax requests.

    The ajax call can result in slower page loads potentially. It causes another request to be made and if other resources haven’t finished downloading this can slow down the retrieval of those other resources. Often times those resources (images, stylesheets, other scripts, etc.) are more important then the data in the ajax request to rendering the initial page.

    • I absolutely agree. This is specifically for a site that loads its most relevant data via an ajax request… The _actual_ fastest way to make the request would be to just include the json at the bottom of the dom in the initial request… There are definitely things to consider when using this technique. I talked alot about how to make sure you don’t have ‘max_connections’ problems in the reply to Patrick’s comment above.

      Certainly for most pages, this isn’t the answer. If your data is the core content of your page (often times mobile applications load in this manner), then this would be the ideal way to load your scripts, barring a native XHR implentation in order to save a framework load.

      To go further, scripts should never block your page. You can get around ‘max_connections’ issues very easily with a wildcard subdomain, and scripts can be loaded with a javascript loader to always make sure your resources are loaded asynchronously. LabJS (http://labjs.com/) offers a low filesize script loader that will do everything possible to load all of your javascript simultaneously and without blocking. I wasn’t talking about dependency management, and was more showing an easy trick, so I didn’t go into detail about this kind of stuff, but you should _certainly_ check it out if you are _ever_ worried about scripts blocking page load. It just doesn’t have to happen…

      Thanks for the feedback!

  13. So here’s a scenario for consideration with this technique. My application relies heavily on jquery and jquery ui so they are loaded in the head. For users with slower connections, I have to provide a modal message that informs the user to wait while the data loads. Since the code is already loading, the ui dialog is the logical choice for the loading screen.

    Ideally, the dialog would show first (or immediately after the stuff appears) and the ajax calls would continue to populate the fields, the tabs build, etc. Can this technique help accomplish this even if one set of ajax calls is synchronous? Does the fact that I’m using a master/content .aspx page and my ajax calls are to webmethods further complicate things?

    • I’m not sure, with a page load of the size it seems like you have, this is going to do too much for you, however this technique isn’t going to hurt anything. This is kind of a last step – save a few extra milliseconds type thing, not really a gigantic performance increase.

      For your load time, I might suggest you just style the page to have a dialog on page load. That way it doesn’t wait for jquery ui to load. Then at the end of the page just hide it with jquery. It stops you from having to load ui at all if that’s the only place, or at least lets you wait until later.

  14. Thanks for the useful tip Alex!

    I’m going to go read your other posts now :)


Leave a comment