Learning jQuery(Fourth Edition)
上QQ阅读APP看书,第一时间看更新

Performing tasks on page load

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

In Chapter 1, Getting Started, we noted that $(document).ready() was jQuery's primary way to perform tasks on page load. It is not, however, the only method at our disposal. The native window.onload event can achieve a similar effect. While the two methods are similar, it is important to recognize their difference in timing, even though it can be quite subtle depending on the number of resources being loaded.

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 feature-rich 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 file has been downloaded and parsed into a DOM tree, the code can run.

Tip

Style loading and code execution

To ensure that the page has also been styled before the JavaScript code executes, it is good practice to place the <link rel="stylesheet"> and <style> tags prior to any <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 those features. 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 is ready to be used earlier with the correct behavior.

Tip

What is loaded and what is not?

Using $(document).ready() is almost always preferred over 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 bind a handler to the load event); the two mechanisms can coexist peacefully.

Handling multiple scripts on one page

The traditional mechanism for registering event handlers through JavaScript (rather than adding handler attributes right in the HTML content) is to assign a function to the DOM element's corresponding property. For example, suppose we had defined the following 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:

window.onload = doStuff;

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 cleanly separated from the markup.

Tip

Referencing versus calling functions

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 parantheses, the name simply identifies, or references, 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 as follows:

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.

Note

To be fair, jQuery doesn't have a monopoly on workarounds to this issue. We can write a JavaScript function that calls the existing onload handler then calls a passed-in handler. This approach avoids conflicts between rival handlers like $(document).ready() does, but lacks some of the other benefits we have discussed. In modern browsers, including Internet Explorer 9, the DOMContentLoaded event can be triggered with the W3C standard document.addEventListener() method. However, if we need to support older browsers as well, jQuery 1.x versions handle the inconsistencies that these browsers present so that we don't have to handle them ourselves.

Alternate syntax for .ready()

The $(document).ready() construct is actually calling the .ready() method on a jQuery object we've constructed from the document DOM element. The $() function provides a shortcut for us as this is a common task. When we pass in a function as the argument to $(), jQuery performs an implicit call to .ready(). We normally write this construct in the following way:

$(document).ready(function() {
  // Our code here...
});

However, we can choose to use the following shorter version instead:

$(function() {
  // Our code here...
});

While the latter syntax is shorter, the longer version makes code more descriptive about what it is doing. For this reason, we will use the longer syntax throughout this book.

Passing an argument to the .ready() callback

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

Fortunately, jQuery provides a method called jQuery.noConflict() to return control of the $ identifier back to other libraries. Typical usage of jQuery.noConflict() follows the following pattern:

<script src="prototype.js"></script>
<script src="jquery.js"></script>
<script>
  jQuery.noConflict();
</script>
<script src="myscript.js"></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 want to use a jQuery method, we need to write 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 using the following syntax:

jQuery(document).ready(function($) {
  // In here, we can use $ like normal!
});

Or we can use the shorter syntax we learned previously:

jQuery(function($) {
  // Code that uses $.
});