We have already seen how to make jQuery react to the loading of a web page. The $(document).ready() event handler can be used to fire off a function's worth of code, but there's a bit more to be said about it.
Timing of code execution
We noted that $(document).ready() was jQuery's way to perform tasks that were typically triggered by JavaScript's built-in onload event. While the two have a similar effect, however, they trigger actions at subtly different times.
The window.onload event
fires when a document is completely downloaded to the browser. This
means that every element on the page is ready to be manipulated by
JavaScript, which is a boon for writing featureful code without worrying
about load order.
On the other hand, a handler registered using $(document).ready()
is invoked when the DOM is completely ready for use. This also means
that all elements are accessible by our scripts, but does not mean that
every associated file has been downloaded. As soon as the HTML has been
downloaded and parsed into a DOM tree, the code can run.
To ensure that the page has also been styled before the JavaScript code executes, it is a good practice to place <link rel="stylesheet"> tags prior to <script> tags within the document's <head> element.
Consider, for example, a
page that presents an image gallery; such a page may have many large
images on it, which we can hide, show, move, and otherwise manipulate
with jQuery. If we set up our interface using the onload
event, users will have to wait until each and every image is completely
downloaded before they can use the page. Even worse, if behaviors are
not yet attached to elements that have default behaviors (such as
links), user interactions could produce unintended outcomes. However,
when we use $(document).ready() for the setup, the interface gets ready to use much earlier with the correct behavior.
Using $(document).ready() is almost always preferable to using an onload
handler, but we need to keep in mind that because supporting files may
not have loaded, attributes such as image height and width are not
necessarily available at this time. If these are needed, we may at times
also choose to implement an onload handler (or more likely, use jQuery to set a handler for the load event); the two mechanisms can coexist peacefully.
Multiple scripts on one page
The traditional mechanism
for registering event handlers through JavaScript (rather than adding
handler attributes right in the HTML) is to assign a function to the DOM
element's corresponding attribute. For example, suppose we had defined
the function:
function doStuff() {
// Perform a task...
}
We could then either assign it within our HTML markup:
<body onload="doStuff();">
Or, we could assign it from within JavaScript code:
Both of these approaches
will cause the function to execute when the page is loaded. The
advantage of the second is that the behavior is more cleanly separated
from the markup.
Note here that when we assign a function as a handler, we use the
function name but omit the trailing parentheses. With the parentheses,
the function is called immediately; without, the name simply identifies the function, and can be used to call it later.
With one function, this strategy works quite well. However, suppose we have a second function:
function doOtherStuff() {
// Perform another task...
}
We could then attempt to assign this function to run on page load:
window.onload = doOtherStuff;
However, this assignment trumps the first one. The .onload attribute can only store one function reference at a time, so we can't add to the existing behavior.
The $(document).ready()
mechanism handles this situation gracefully. Each call to the method
adds the new function to an internal queue of behaviors; when the page
is loaded all of the functions will execute. The functions will run in
the order in which they were registered.
To be fair, jQuery doesn't have a monopoly on workarounds to this issue.
We can write a JavaScript function that forms a new function that calls
the existing onload handler, then calls a passed-in handler. This approach, used for example by Simon Willison's addLoadEvent(), avoids conflicts between rival handlers like $(document).ready() does, but lacks some of the other benefits we have discussed. Browser-specific methods such as document.addEventListener() and document.attachEvent()
offer similar functionality, but jQuery allows us to accomplish this
task without concerning ourselves with browser inconsistencies.
Shortcuts for code brevity
The $(document).ready() construct is actually calling the .ready() method on a jQuery object we've constructed from the document DOM element. The $()
factory function provides a shortcut for us as this is a common task. When called with no arguments, the function behaves as though document were passed in. This means that instead of:
$(document).ready(function() {
// Our code here...
});
we can write:
$().ready(function() {
// Our code here...
});
In addition, the factory function can take another function as an argument. When we do this, jQuery performs an implicit call to .ready(), so for the same result we can write:
$(function() {
// Our code here...
});
While these other syntaxes
are shorter, the authors recommend the longer version to make it
clearer as to what the code is doing.
Coexisting with other libraries
In some cases, it may
prove useful to use more than one JavaScript library on the same page.
Since many libraries make use of the $ identifier (since it is short and convenient), we need a way to prevent collisions between these names.
Fortunately, jQuery provides a method called .noConflict() to return control of the $ identifier back to other libraries. Typical usage of .noConflict() follows the following pattern:
<script src="prototype.js" type="text/javascript"></script>
<script src="jquery.js" type="text/javascript"></script>
<script type="text/javascript">
jQuery.noConflict();
</script>
<script src="myscript.js" type="text/javascript"></script>
First, the other library (Prototype in this example) is included. Then, jQuery itself is included, taking over $ for its own use. Next, a call to .noConflict() frees up $,
so that control of it reverts to the first included library
(Prototype). Now in our custom script we can use both
libraries—but whenever we need to use a jQuery method, we need
to use jQuery instead of $ as an identifier.
The .ready() method
has one more trick up its sleeve to help us in this situation. The
callback function we pass to it can take a single parameter: the jQuery
object itself. This allows us to effectively rename it without fear of
conflicts:
jQuery(document).ready(function($) {
// In here, we can use $ like normal!
});
Or, using the shorter syntax we learned above:
jQuery(function($) {
// Code that uses $.
});