Earlier today, I read Stop paying your jQuery tax, an excellent article by Sam Saffron which explains why it’s a great idea to move all of your external JavaScripts to the bottom of the HTML document (just before the closing body tag). Near the end of the article, he proposes a method which allows you to continue to use jQuery’s DOM ready method anywhere in your document, even though you’ve moved jQuery itself to the bottom. His method is essentially this:
- In the
head, include a script that:- Defines an array
- Creates a fake
$function that pushes its argument to the array
- In the
body, just after you include jQuery, include a script that:- Uses jQuery to loop over the array’s contents
- …and calls the real
$function, passing in the argument.
I decided to have a play around with the code examples Sam gave and I realised that it only caters for one of jQuery’s possible ways of binding to DOM ready:
1
| |
jQuery also allows the following:
1 2 3 4 5 | |
Solution
With this in mind, I attempted to build upon Sam’s concept, but come up with a solution that covers all four possibilities. Here’s what I came up with:
1 2 3 4 5 | |
OK, that looks like an unreadable mess, so I’ll expand it out (with nicer variable/function names) and take you through it. Expanding the head script, we have:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | |
If you look at jQuery’s .ready() method documentation, it explains that if handlers bound to DOM ready using the .bind() function are actually triggered after all other handlers have been triggered. This is the reason we have two queues - to respect that behaviour.
Expanding the body (just after jQuery) script, we have:
1 2 3 4 5 6 7 8 9 10 11 | |
In exactly the same way as Sam’s example, we use jQuery’s .each() method to properly bind all of our queued handlers to DOM ready, but because $(document).bind("ready", handler) may have been called earlier, we bind these too in the correct way.
Example
Here’s a quick example of how to use the scripts, followed by the console output it produces.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
1 2 3 4 | |
Note that even though Example D is the first example, it uses $(document).bind("ready", handler), so it is queued separately, and is executed after the other three examples. It behaves exactly as jQuery intends.
I hope you find this useful. Please leave suggestions (or point out errors) in the comments below.
Updated on 02/03/2012:
In the comments, BRUNOa A suggested a couple of performance enhancing changes to my solution. As an extra enhancement, I’m also passing the document object as an argument into the anonymous functions. The original implementation still exists as a gist.