CHAPTER 5

image

jQuery Basics

In this chapter, I will introduce you to your first jQuery script. The script is simple, but it demonstrates many of the most important characteristics of jQuery, including how you select elements in the document, how such selections are presented to you, and the nature of the relationship between jQuery and the built-in DOM API that is part of the HTML specification. Table 5-1 provides the summary for this chapter.

Table 5-1. Chapter Summary

Problem Solution Listing
Add jQuery to an HTML document. Use the link element to import the jQuery element, linking either to your web server or to a CDN. Add a script element to define your jQuery script. 1
Dynamically select the jQuery 1.x or 2.x lines. Use conditional comments. 2
Select elements in the document. Pass a CSS selector to the $ or jQuery function. 3, 4, 10
Rename the $ function. Use the noConflict method. 5, 6
Defer execution of your jQuery script until the document has been loaded. Register a handler for the ready event on the global document variable or pass a function to the $ function. 7, 8
Take control of when the ready event is triggered. Use the holdReady event. 9
Restrict element selection to part of the document. Pass a context to the $ function. 11, 12
Determine the context used to create a jQuery object. Read the context property. 13
Create a jQuery object from HTMLElement objects. Pass the HTMLElement objects as the argument to the $ function. 14
Enumerate the contents of a jQuery object. Treat the jQuery object as an array or use the each method. 15, 16
Find a specific element in a jQuery element. Use the index or get methods. 17–19
Apply an operation to multiple elements in the document. Use a jQuery method on a jQuery object. 20
Apply multiple operations to a jQuery object. Chain methods calls together. 21–23
Handle an event. Use one of the jQuery event handler methods. 24

JQUERY CHANGES SINCE THE LAST EDITION

In the first edition of this book, I described the use of the selector property to get a selector string from jQuery that could be used to repeat queries in the future. As of jQuery 1.9, the selector property has been deprecated and should not be used.

The selector engine that jQuery 1.9/2.0 uses (known as Sizzle) implements support for some new CSS3 selectors even in browsers that don't support them. The selectors are: :nth-last-child, :nth-of-type, :nth-last-of-type, :first-of-type, :last-of-type, :only-of-type, :target, :root, and :lang.

Setting Up jQuery

The first thing you need to do with jQuery is add it to the document you want to work with. Listing 5-1 shows the flower shop example document you first saw in Part 1, with the addition of the jQuery library.

Listing 5-1.  The Flower Shop Example Document

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <link rel="stylesheet" type="text/css" href="styles.css"/>
    <script src="jquery-2.0.2.js" type="text/javascript"></script>
</head>
<body>
    <h1>Jacqui's Flower Shop</h1>
    <form method="post">
        <div id="oblock">
            <div class="dtable">
                <div id="row1" class="drow">
                    <div class="dcell">
                        <img src="aster.png"/><label for="aster">Aster:</label>
                        <input name="aster" value="0" required>
                    </div>
                    <div class="dcell">
                        <img src="daffodil.png"/><label for="daffodil">Daffodil:</label>
                        <input name="daffodil" value="0" required >
                    </div>
                    <div class="dcell">
                        <img src="rose.png"/><label for="rose">Rose:</label>
                        <input name="rose" value="0" required>
                    </div>
                </div>
                <div id="row2" class="drow">
                    <div class="dcell">
                        <img src="peony.png"/><label for="peony">Peony:</label>
                        <input name="peony" value="0" required>
                    </div>
                    <div class="dcell">
                        <img src="primula.png"/><label for="primula">Primula:</label>
                        <input name="primula" value="0" required>
                    </div>
                    <div class="dcell">
                        <img src="snowdrop.png"/><label for="snowdrop">Snowdrop:</label>
                        <input name="snowdrop" value="0" required>
                    </div>
                </div>
            </div>
        </div>
        <div id="buttonDiv"><button type="submit">Place Order</button></div>
    </form>
</body>
</html>

To help maintain focus on the content, I have moved the CSS styles to a separate style sheet called styles.css, as demonstrated in Chapter 3. You can see how I have added the jQuery library to the document, as follows:

...
<script src="jquery-2.0.2.js" type="text/javascript"></script>
...

Once you have selected the jQuery line that you will be using (the 1.x or 2.x line, as described in Chapter 3), you will have the choice of two files from jquery.com – one with a .js file extension and the other with .min.js. For version of the 2.x line that is current as I write this, the files are called jquery-2.0.2.js and jquery-2.0.2.min.js.

The jquery-2.0.2.js file is the one generally used during the development of a web site or application. This file is around 240KB and standard JavaScript code. You can open and read the file to learn about how jQuery implements its features and use the browser debugger to figure out what is going wrong if you encounter problems in your code.

image Tip  jQuery is actively developed, and so by the time you read this a later version will almost certainly have been released. But don’t worry – despite all of the talk of deprecated methods and split development lines, the jQuery API is incredibly stable, and all of the techniques that I show you in this book will continue to work as jQuery 1.x and 2.x continue to mature.

The other file, jquery.2.0.2.min.js, is intended for use when you deploy your site or web application to users. It contains the same JavaScript code but has been minified, meaning that all of the whitespace characters have been removed, and the meaningful variable names have been replaced with single-character names to save space. The minified script is almost impossible to read for the purposes of debugging, but it is much smaller. The minification process reduces the size of the file to about 82KB and if you are serving up a lot of pages that rely on jQuery, then this difference can save you significant amounts of bandwidth.

image Tip  You can download a source map (which has the .min.map extension) from jquery.com. Source maps allow minified code to be more easily debugged. They are a new idea and are not that widely implemented as I write this. You can see a simple demonstration at http://www.html5rocks.com/en/tutorials/developertools/sourcemaps.

USING A CDN FOR JQUERY

An alternative to storing the jQuery library on your own web servers is to use a public content delivery network (CDN) that hosts jQuery. A CDN is a distributed network of servers that deliver files to the user using the server that is closest to them. There are a couple of benefits to using a CDN. The first is a faster experience to the user, because the jQuery library file is downloaded from the server closest to them, rather than from your servers. Often a download won’t be required at all: jQuery is so popular that the user’s browser may have already cached the library from another application or site that also uses jQuery. The second benefit is that none of your bandwidth is used delivering jQuery to the user. For high-traffic sites, this can be a significant cost savings.

When using a CDN, you must have confidence in the CDN operator. You want to be sure that the user receives the file they are supposed to and that service will always be available. Google and Microsoft both provide CDN services for jQuery (and other popular JavaScript libraries) free of charge. Both companies have good experience running highly available services and are unlikely to deliberately tamper with the jQuery library. You can learn about the Microsoft service at www.asp.net/ajaxlibrary/cdn.ashx and about the Google service at http://code.google.com/apis/libraries/devguide.html.

The CDN approach isn’t suitable for applications that are delivered to users within an intranet because it causes all the browsers to go to the Internet to get the jQuery library, rather than access the local server, which is generally closer and faster and has lower bandwidth costs.

Using Conditional Comments

In Listing 5-1, I included only version 2.0.2 of the jQuery library. This version offers the best performance but it doesn’t support older versions of Internet Explorer, as described in Chapter 1.

The good news is that you don’t have to choose between performance and compatibility. There is a technique that dynamically selects between the 1.x and 2.x jQuery libraries automatically, using a feature called conditional comments that Microsoft created as a nonstandard enhancement to HTML for Internet Explorer 5. In Listing 5-2, you can see how I have applied the conditional comments feature to the example HTML document.

Listing 5-2.  Using Conditional Comments to Dynamically Select between jQuery 1.x and 2.x

...
<head>
    <title>Example</title>
    <link rel="stylesheet" type="text/css" href="styles.css"/>
    <!--[if lt IE 9]>
        <script src="jquery-1.10.1.js" type="text/javascript"></script>
    <![endif]-->
    <!--[if gte IE 9]><!-->
        <script src="jquery-2.0.2.js" type="text/javascript"></script>
    <!--<![endif]-->
</head>
...

The conditional comments will load jQuery 1.10.1 for versions prior to Internet Explorer 9 and jQuery 2.0.2 for all other browsers. You should take care to copy these comments exactly – it is easy to make a mistake.

image Tip  I am not going to go into conditional comments in this book, but you can learn more about them at http://en.wikipedia.org/wiki/Conditional_comment.

A First jQuery Script

Now that I have added the jQuery library to the document, I can write some JavaScript that uses jQuery functionality. Listing 5-3 contains a simple script element that shows off some of the basic jQuery features.

Listing 5-3. A First jQuery Script

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <link rel="stylesheet" type="text/css" href="styles.css"/>
    <script src="jquery-2.0.2.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            $("img:odd").mouseenter(function (e) {
                $(this).css("opacity", 0.5);
            }).mouseout(function (e) {
                $(this).css("opacity", 1.0);
            });
        });
    </script>
</head>
<body>
    <h1>Jacqui's Flower Shop</h1>
    <form method="post">
        <div id="oblock">
            <div class="dtable">
                <div id="row1" class="drow">
                    <div class="dcell">
                        <img src="aster.png"/><label for="aster">Aster:</label>
                        <input name="aster" value="0" required>
                    </div>
                    <div class="dcell">
                        <img src="daffodil.png"/><label for="daffodil">Daffodil:</label>
                        <input name="daffodil" value="0" required >
                    </div>
                    <div class="dcell">
                        <img src="rose.png"/><label for="rose">Rose:</label>
                        <input name="rose" value="0" required>
                    </div>
                </div>
                <div id="row2" class="drow">
                    <div class="dcell">
                        <img src="peony.png"/><label for="peony">Peony:</label>
                        <input name="peony" value="0" required>
                    </div>
                    <div class="dcell">
                        <img src="primula.png"/><label for="primula">Primula:</label>
                        <input name="primula" value="0" required>
                    </div>
                    <div class="dcell">
                        <img src="snowdrop.png"/><label for="snowdrop">Snowdrop:</label>
                        <input name="snowdrop" value="0" required>
                    </div>
                </div>
            </div>
        </div>
        <div id="buttonDiv"><button type="submit">Place Order</button></div>
    </form>
</body>
</html>

This is a short script, but it demonstrates some of the most important features and characteristics of jQuery. I’ll break down the script line by line in this chapter, but it will take you the rest of the part of this book to fully understand all of the functional areas that this script touches upon. To start with, Figure 5-1 shows the effect that this script creates.

9781430263883_Fig05-01.jpg

Figure 5-1. Changing image opacity

image Tip  You will notice that I have returned to the explicit use of jQuery 2.0.2 in the listing – which I will do for most of the rest of this book. I want to keep the examples clear and simple, but I recommend you use the conditional comments technique, which I find useful in my own projects.

The script changes the opacity of the daffodil, peony, and snowdrop images when the mouse is moved over them. This has the effect of making the image look a little brighter and washed out. When the mouse is moved away from the image, the opacity returns to its previous value. The images for the aster, rose, and primula are unaffected.

Understanding the jQuery $ Function

You access jQuery through the $(...) function, which I will refer to as the $ function for simplicity. The $ function is the entry point to the wonderful world of jQuery and is a shorthand for the jQuery function. You can rewrite the example script to use the full function name if you prefer, as shown in Listing 5-4.

Listing 5-4.  Using the jQuery Function in Place of the Shorthand

...
<script type="text/javascript">
    jQuery(document).ready(function () {
        jQuery("img:odd").mouseenter(function(e) {
           jQuery(this).css("opacity", 0.5);
        }).mouseout(function(e) {
           jQuery(this).css("opacity", 1.0);
        });
    });
</script>
...

This script provides the same functionality as the previous example. It requires slightly more typing but has the advantage of making the use of jQuery explicit.

This can be useful because jQuery is not the only JavaScript library that uses the $ notation, which can cause problems when you use multiple libraries in the same document. You can make jQuery relinquish control of the $ by calling the jQuery.noConflict method, as shown in Listing 5-5.

Listing 5-5.  Releasing jQuery’s Control of $

...
<script type="text/javascript">
    jQuery.noConflict();
    jQuery(document).ready(function () {
        jQuery("img:odd").mouseenter(function(e) {
           jQuery(this).css("opacity", 0.5);
        }).mouseout(function(e) {
           jQuery(this).css("opacity", 1.0);
        });
    });
</script>
...

You can also define your own shorthand notation. You do this by assigning the result of the noConflict method to a variable, as shown in Listing 5-6.

Listing 5-6.  Using an Alternative Shorthand

...
<script type="text/javascript">
    var jq = jQuery.noConflict();
    jq(document).ready(function () {
        jq("img:odd").mouseenter(function(e) {
           jq(this).css("opacity", 0.5);
        }).mouseout(function(e) {
           jq(this).css("opacity", 1.0);
        });
    });
</script>
...

In this example, I created my own shorthand, jq, and then used this shorthand throughout the rest of my script.

image Tip  I will be using the $ notation throughout this book, since it is the normal convention for jQuery (and because I won’t be using any other library that wants control of $).

Irrespective of how you refer to the main jQuery function, you can pass the same set of arguments, the most important of which I have described in Table 5-2. All of these arguments are described later in this chapter except the last one, which is described in Chapter 7.

Table 5-2. Arguments to the Main jQuery Function

Argument Description
$(function) Specifies a function to be executed when the DOM is ready.
$(selector) $(selector, context) Selects elements from the document.
$(HTMLElement) $(HTMLElement[]) Creates a jQuery object from an HTMLElement or an array of HTMLElement objects.
$() Creates an empty selection.
$(HTML) $(HTML, map) Creates new elements from a fragment of HTML with an optional map object to define attributes. See Chapter 7 for details.

Waiting for the Document Object Model

In Chapter 2, I placed the script element at the end of the document so that the browser would create all of the objects in the DOM before executing my JavaScript code. You can neatly avoid this issue by using jQuery, by using the following technique:

...
<script type="text/javascript">
    $(document).ready(function () {
        // ... code to execute...
    });
</script>
...

Instead of putting the JavaScript statements directly in the script element, I have passed the document object (which I introduced in Chapter 1) to the $ function and called the ready method, passing in a function that I want to be executed only when the browser has loaded all of the content in the HTML document. Listing 5-7 shows how I applied this technique in the example HTML document.

Listing 5-7.  Waiting for the DOM

...
<script type="text/javascript">
    $(document).ready(function () {
        $("img:odd").mouseenter(function (e) {
            $(this).css("opacity", 0.5);
        }).mouseout(function (e) {
            $(this).css("opacity", 1.0);
        });
    });
</script>
...

Using the ready function means I can place the script element wherever I want in the HTML document safe in the knowledge that jQuery will prevent the function from being executed prematurely. I like to put my script elements in the head element of the HTML document, but that’s just my preference.

image Note  Passing a function to the ready method creates a handler for the jQuery ready event. I cover jQuery events fully in Chapter 9. For the moment, please accept that the function you pass to the ready method will be invoked when the document is loaded and the DOM is ready for use.

FORGETTING THE FUNCTION

A common error is to omit the function part of this incantation and just pass a series of JavaScript statements to the ready method. This doesn’t work and the statements are executed by the browser immediately, rather than when the DOM is ready. Consider the following script:

...
<script type="text/javascript">
    function countImgElements() {
        return $("img").length;
    }
    $(document).ready(function() {
      console.log("Ready function invoked. IMG count: " + countImgElements());
    });
    $(document).ready(
      console.log("Ready statement invoked. IMG count: " + countImgElements())
    );
</script>

I call the ready method twice, once with a function and once just passing in a regular JavaScript statement. In both cases, I call the countImgElements function, which returns the number of img elements that are present in the DOM. (Don’t worry about how this method works for the moment. I explain the call to the length property later in this chapter.) When I load the document, the script is executed, and the following output is written to the console:

Ready statement invoked. IMG count: 0
Ready function invoked. IMG count: 6

As you can see, the statement without the function is executed as the document is loaded and before the browser has discovered the img elements in the document and created the corresponding DOM objects.

Using the Alternative Notation

You can pass your function as the parameter to the jQuery $ function if you prefer. This has the same effect as using the $(document).ready approach. Listing 5-8 provides a demonstration.

Listing 5-8. Deferring Execution of a Function Until the DOM Is Ready

...
<script type="text/javascript">
    $(function() {
        $("img:odd").mouseenter(function(e) {
           $(this).css("opacity", 0.5);
        }).mouseout(function(e) {
           $(this).css("opacity", 1.0);
        })
    });
</script>
...

Deferring the ready Event

You can control when the ready event is triggered by using the holdReady method. This can be useful if you need to load external resources dynamically (an unusual and advanced technique). The holdReady method must be called before the ready event is triggered and can then be called again when you are ready. Listing 5-9 gives an example of using this method.

Listing 5-9.  Using the holdReady Method

...
<script type="text/javascript">
  
    $.holdReady(true);
  
    $(document).ready(function() {
        console.log("Ready event triggered");
        $("img:odd").mouseenter(function(e) {
           $(this).css("opacity", 0.5);
        }).mouseout(function(e) {
           $(this).css("opacity", 1.0);
        })
    });
      
    setTimeout(function() {
        console.log("Releasing hold");
        $.holdReady(false);
    }, 5000);
      
</script>
...

I call the holdReady method at the start of the script element, passing in true as the argument that indicates that I want the ready event to be held. I then define the function I want to be called when the ready event is fired (this is the same set of statements that I opened the chapter with, which alter the opacity of some of the images).

Finally, I use the setTimeout method to invoke a function after five seconds. This function calls the holdReady method with an argument of false, which tells jQuery to trigger the ready event. The net effect is that I delay the ready event for five seconds. I have added some debug messages that write the following output to the console when the document is loaded into the browser:

Releasing hold
Ready event triggered

image Tip  You can call the holdReady method multiple times, but the number of calls to the holdReady method with the true argument must be balanced by the same number of calls with the false argument before the ready event will be triggered.

Selecting Elements

One of the most important areas of jQuery functionality is how you select elements from the DOM. In the example script, I located all of the oddimg elements, as shown in Listing 5-10.

Listing 5-10. Selecting Elements from the DOM

...
<script type="text/javascript">
    $(document).ready(function() {
        $("img:odd").mouseenter(function(e) {
           $(this).css("opacity", 0.5);
        }).mouseout(function(e) {
           $(this).css("opacity", 1.0);
        })
    });
</script>
...

To select elements, you simply pass a selector to the $ function. jQuery supports all of the CSS selectors I described in Chapter 3, plus some additional ones that give you some handy fine-grained control. In the example I used the :odd pseudo-selector, which selects the odd-numbered elements matched by the main part of the selector (img in this case, which selects all of the img elements, as described in Chapter 3). The :odd selector is zero-based, meaning that the first element is considered to be even. This can be a little confusing at first. Table 5-3 lists the most useful jQuery selectors.

Table 5-3. jQuery Extension Selectors

Selector Description
:animated Selects all elements that are being animated.
:contains(text) Selects elements that contain the specified text.
:eq(n) Selects the element at the nth index (zero-based).
:even Selects all the event-numbered elements (zero-based).
:first Selects the first matched element.
:gt(n) Selects all of the elements with an index greater than n (zero-based).
:has(selector) Selects elements that contain at least one element that matches the specified selector.
:last Selects the last matched element.
:lt(n) Selects all of the elements with an index smaller than n (zero-based).
:odd Selects all the odd-numbered elements (zero-based).
:text Selects all text elements.

image Tip  You can create an empty selection by calling the $ function without any arguments ($()). I mention this for completeness, but it is not a feature that I have ever had cause to use.

I have called these the most useful because they define functionality that would be difficult to re-create using CSS selectors. These selectors are used just like the CSS pseudo-selectors. They can be used on their own, in which case they are applied to all of the elements in the DOM, like this:

...
$(":even")
...

or combined with other selectors to narrow the selection, like this:

...
$("img:even")
...

jQuery also defines selectors that select elements based on type, as described in Table 5-4.

Table 5-4. jQuery Type Extension Selectors

Selector Description
:button Selects all buttons.
:checkbox Selects all check boxes.
:file Selects all file elements.
:header Selects all header elements (h1, h2, and so on).
:hidden Selects all hidden elements.
:image Selects all image elements.
:input Selects all input elements.
:last Selects the last matched element.
:parent Selects all of the elements that are parents to other elements.
:password Selects all password elements.
:radio Selects all radio elements.
:reset Selects all elements that reset a form.
:selected Selects all elements that are selected.
:submit Selects all form submission elements.
:visible Selects all visible elements.

CONSIDERING SELECTOR PERFORMANCE

If you spend any time reading about jQuery, you are sure to encounter a discussion about selector performance. A great many people spend a great deal of time comparing different ways of expressing selectors to squeeze the maximum performance out of jQuery.

My view is simple: it should not matter – and if it does matter, then it is a sign of some other problem. The performance of jQuery is pretty good, especially on the latest versions of the mainstream browsers that have fast JavaScript implementations. When I see project teams that are trying to optimize selector performance, it is usually because they are dealing with huge numbers of HTML elements where it can take hundreds of milliseconds to perform a selection – and when several such selections are required to perform an operation, we start to get into the length of delay that users notice.

The real problem isn’t jQuery – it is the practice of sending more content to the browser than it can be reasonably expected to cope with. Browsers are increasingly powerful and capable, but there are limits to what they can be expected to do, especially older browsers and those running on mobile devices.

If you find yourself struggling to select elements quickly enough, don’t try to optimize your use of selectors. Instead, reconsider your use of HTML elements: look for ways to minimize the content sent to the browser, take on some of the processing at the server and stop treating web applications as though they were desktop applications.

Narrowing the Selection with a Context

By default, jQuery searches the entire DOM for elements, but you can narrow the scope of a selection by providing an additional argument to the $ function. This gives the search a context, which is used as the starting point for matching elements, as shown in Listing 5-11.

Listing 5-11. Narrowing a Search with a Context

...
<script type="text/javascript">
    $(document).ready(function() {
                  
        $("img:odd",$(".drow")).mouseenter(function(e) {
           $(this).css("opacity", 0.5);
        }).mouseout(function(e) {
           $(this).css("opacity", 1.0);
        })
    });
</script>
...

In this example, I use one jQuery selection as the context for another. The context is evaluated first, and it matches all of the elements that are members of the drow class. This set of elements is then used as the context for the img:odd selector.

When you supply a context that contains multiple elements, then each element is used as a starting point in the search. There is an interesting subtlety in this approach. The elements that match the context are gathered together, and then the main selection is performed. In the example, this means the img:odd selector is applied to the results of the drow selector, which means that the odd-numbered elements are not the same as when you search the entire document. The net result is that the opacity effect is applied to the odd-numbered img elements in each div element in the drow class, selecting the daffodil and primula images. When I omitted the context in the earlier example, the effect was applied to the daffodil, peony, and snowdrop images.

If you just want to match elements starting at a given point in the document, then you can use an HTMLElement object as the context. Listing 5-12 contains an example. I show you how to easily switch between the jQuery world and HTMLElement objects in the next section.

Listing 5-12.  Using an HTMLElement as the Context

...
<script type="text/javascript">
    $(document).ready(function() {
        var elem = document.getElementById("oblock");

        $("img:odd",elem).mouseenter(function(e) {
           $(this).css("opacity", 0.5);
        }).mouseout(function(e) {
           $(this).css("opacity", 1.0);
        })
    });
</script>
...

The script in this example searches for odd-numbered img elements, limiting the search to those elements that are descended from the element whose id is oblock.

Of course, you could achieve the same effect using the descendant CSS selector. The benefit of this approach arises when you want to narrow a search programmatically, without having to construct a selector string. A good example of such a situation is when handling an event. You can learn more about events (and see how HTMLElement objects arise in this situation) in Chapter 9.

Understanding the Selection Result

When you use jQuery to select elements from the DOM, the result from the $ function is a confusingly named jQuery object, which represents zero or more DOM elements. In fact, when you perform a jQuery operation that modifies one or more elements, the result is likely to be a jQuery object, which is an important characteristic that I’ll return to shortly.

The methods and properties that are defined by the jQuery object are essentially the contents for the rest of the book, but there are some basic members that I can cover in this chapter, as described in Table 5-5.

Table 5-5. Basic jQuery Object Members

Selector Description Returns
context Returns the set of elements used as the search context. HTMLElement
each(function) Performs the function on each of the selected elements. jQuery
get(index) Gets the HTMLElement object at the specified index. HTMLElement
index(HTMLElement) Returns the index of the specified HTMLElement. number
index(jQuery) Returns the index of the first element in the jQuery object. number
index(selector) Returns the index of the first element in the jQuery object in the set of elements matched by the selector number
length Returns the number of elements contained by the jQuery object. number
size() Returns the number of elements in the jQuery object. number
toArray() Returns the HTMLElement objects contained by the jQuery object as an array. HTMLElement[]

Determining the Context

The context property provides you with details of the context used when the jQuery was created. If a single HTMLElement object was used as the context, then the context property will return that HTMLElement. If no context was used or if multiple elements were used (as in the example I used earlier in the chapter), then the context property returns undefined instead. Listing 5-13 shows this property in use.

Listing 5-13.  Determining the Context for a jQuery Object

...
<script type="text/javascript">
    $(document).ready(function() {
        var jq1 = $("img:odd");
        console.log("No context: " +jq1.context.tagName);
                  
        var jq2 = $("img:odd", $(".drow"));
        console.log("Multiple context elements: " +jq2.context.tagName);
                  
        var jq3 = $("img:odd", document.getElementById("oblock"));
        console.log("Single context element: " +jq3.context.tagName);
    });
</script>
...

This script selects elements using no context, multiple context objects, and a single context object. The output is as follows:

No context: undefined
Multiple context elements: undefined
Single context element: DIV

Dealing with DOM Objects

jQuery doesn’t replace the DOM; it just makes it a lot easier to work with. The HTMLElement objects (which I introduced in Chapter 2) are still used, and the jQuery library makes it easy to switch between jQuery objects and DOM objects. The ease with which you can move from the traditional DOM to jQuery and back is part of the elegance of jQuery and helps you maintain compatibility with non-jQuery scripts and libraries.

Creating jQuery Objects from DOM Objects

You can create jQuery objects by passing an HTMLElement object or an array of HTMLElement objects as the argument to the $ function. This can be useful when dealing with JavaScript code that isn’t written in jQuery or in situations where jQuery exposes the underlying DOM objects, such as event processing. Listing 5-14 contains an example.

Listing 5-14.  Creating jQuery Objects from DOM Objects

...
<script type="text/javascript">
    $(document).ready(function() {
          
        var elems = document.getElementsByTagName("img");
          
        $(elems).mouseenter(function(e) {
           $(this).css("opacity", 0.5);
        }).mouseout(function(e) {
           $(this).css("opacity", 1.0);
        })
    });
</script>
...

In this example, I select the img elements in the document using the document.getElementsByTagName method, rather than using jQuery directly with a selector. I pass the results of this method (which is a collection of HTMLElement objects) to the $ function, which returns a regular jQuery object that I can use just as in the previous examples.

This script also demonstrates how you can create a jQuery object from a single HTMLElement object:

...
$(this).css("opacity", 1.0);
...

When you are handling events, jQuery sets the value of the this variable to the HTMLElement that is processing the event. I describe the jQuery event support in Chapter 9, so I don’t want to get into the subject in this chapter (although I do mention the functions that contain these statements again a little later in this chapter).

Treating a jQuery Object as an Array

You can treat a jQuery object as an array of HTMLElement objects. This means you get to use the advanced features that jQuery provides but still access the DOM directly. You can use the length property or the size method to determine how many elements are collected in the jQuery object and access individual DOM objects by using an array-style index (using the [ and ] brackets).

image Tip  You can use the toArray method to extract the HTMLElement objects from the jQuery object as an array. I like to use the jQuery object itself, but sometimes it is useful to work with the DOM objects, such as when dealing with legacy code that wasn’t written using jQuery.

Listing 5-15 shows how you can enumerate the contents of a jQuery object to access the HTMLElement objects contained within.

Listing 5-15.  Treating a jQuery Object as an Array

...
<script type="text/javascript">
    $(document).ready(function() {
        var elems = $("img:odd");
        for (var i = 0; i < elems.length; i++) {
            console.log("Element: " + elems[i].tagName + " " + elems[i].src);
        }
    });
</script>
...

In the listing, I use the $ function to select the odd-numbered img elements and enumerate the selected elements to print out the value of the tagName and src properties to the console. The results are as follows:

Iterate a Function over DOM Objects

The each method lets you define a function that is performed for each DOM object in the jQuery object. Listing 5-16 gives a demonstration.

Listing 5-16.  Using the each Method

...
<script type="text/javascript">
    $(document).ready(function() {
        $("img:odd").each(function(index, elem) {
            console.log("Element: " + elem.tagName + " " + elem.src);
        });
    });
</script>
...

jQuery passes two arguments to the specified function. The first is the index of the element in the collection, and the second is the element object itself. In this example, I write the tag name and the value of the src property to the console, producing the same results as the previous script:

Finding Indices and Specific Elements

The index method lets you find the index of an HTMLElement in a jQuery object. You can pass the index that you want using either an HTMLElement or jQuery object as the argument. When you use a jQuery object, the first matched element is the one whose index is returned. Listing 5-17 gives a demonstration.

Listing 5-17.  Locating the Index of an HTMLElement

...
<script type="text/javascript">
    $(document).ready(function() {
          
        var elems = $("body *");
          
        // find an index using the basic DOM API
        var index = elems.index(document.getElementById("oblock"));
        console.log("Index using DOM element is: " + index);
          
        // find an index using another jQuery object
        index = elems.index($("#oblock"));
        console.log("Index using jQuery object is: " + index);
    });
</script>
...

In this example, I locate a method using the DOM API’s getElementById method to find a div element by the id attribute value. This returns an HTMLElement object. I then use the index method on a jQuery object to find the index of the object that represents the div element. I repeat the process using a jQuery object, which I obtain through the $ function. I write the results from both approaches to the console, which produces the following results:

Index using DOM element is: 2
Index using jQuery object is: 2

You can also pass a string to the index method. When you do this, the string is interpreted as a selector. However, this approach causes the index method to behave in a different way than the previous example. Listing 5-18 provides a demonstration.

Listing 5-18.  Using the Selector Version of the index Method

...
<script type="text/javascript">
    $(document).ready(function() {
                  
        var imgElems = $("img:odd");
        // find an index using a selector
        index = imgElems.index("body *");
        console.log("Index using selector is: " + index);
  
        // perform the same task using a jQuery object
        index = $("body *").index(imgElems);
        console.log("Index using jQuery object is: " + index);
  
    });
</script>
...

When you pass a string to the index method, the order in which the collection of elements is used changes. jQuery matches elements using the selector and then returns the index in the matched elements of the first element in the jQuery object on which you called the index method. This means that this statement:

...
index = imgElems.index("body *");
...

is equivalent to this statement:

...
index = $("body *").index(imgElems);
...

In essence, passing a string argument reverses the way in which the two sets of elements are considered. The listing produces the following result:

Index using selector is: 10
Index using jQuery object is: 10

image Tip  We can use the index method without an argument to get the position of an element relative to its siblings. This can be useful when using jQuery to explore the DOM, which is the topic of Chapter 7.

The get method is the complement to the index method, such that you specify an index and receive the HTMLElement object at that position in the jQuery object. This has the same effect as using the array-style index I described earlier in this chapter. Listing 5-19 provides a demonstration.

Listing 5-19.  Getting the HTMLElement Object at a Given Index

...
<script type="text/javascript">
    $(document).ready(function() {
        var elem = $("img:odd").get(1);
        console.log("Element: " + elem.tagName + " " + elem.src);
    });
</script>
...

In this script, I select the odd-numbered img elements, use the get method to retrieve the HTMLElement object at index 1, and write the value of the tagName and src properties to the console. The output from this script is as follows:

Modifying Multiple Elements and Chaining Method Calls

One of the features that make jQuery so concise and expressive is that calling a method on a jQuery object usually modifies all of the elements that the object contains. I say usually, because some methods perform operations that don’t apply to multiple elements, and you’ll see examples of this in later chapters. Listing 5-20 shows how to perform an operation on multiple elements using the DOM API.

Listing 5-20. Operating on Multiple Elements Using the DOM API

...
<script type="text/javascript">
    $(document).ready(function() {
          
        var labelElems = document.getElementsByTagName("label");
        for (var i = 0; i < labelElems.length; i++) {
            labelElems[i].style.color = "blue";
        }
    });
</script>
...

The statements in the script element select all of the label elements and set the value of the CSS color property to blue. Listing 5-21 shows how to perform the same task using jQuery.

Listing 5-21.  Operating on Multiple Elements Using the jQuery

...
<script type="text/javascript">
    $(document).ready(function () {
        $("label").css("color", "blue");
    });
</script>
...

I can perform the task using a single jQuery statement, which is less effort than using the DOM API—not a huge difference, I admit, but it mounts up in a complex web application. The jQuery statement is also easier to read and understand, which helps with the long-term maintenance of the JavaScript code.

Another nice feature of the jQuery object is that it implements a fluent API. This means that whenever you call a method that modifies the contents of the object, the result of the method is another jQuery object. This may seem simple, but it allows you to perform method chaining, as shown in Listing 5-22.

Listing 5-22.  Method Chaining Method Calls On a jQuery Object

...
<script type="text/javascript">
    $(document).ready(function() {
  
        $("label").css("color", "blue").css("font-size", ".75em");
          
        var labelElems = document.getElementsByTagName("label");
        for (var i = 0; i < labelElems.length; i++) {
            labelElems[i].style.color = "blue";
            labelElems[i].style.fontSize = ".75em";
        }
    });
</script>
...

In this example, I create a jQuery object using the $ function, call the css method to set a value for the color property, and then call the css method again, this time to set the font-size property. I have also shown the equivalent addition using the basic DOM API. You can see that it doesn’t require much work to achieve the same effect, because you already have a for loop that is enumerating the selected elements.

You start to get real benefit from the fluent API when chaining methods that make more substantial changes to the set of elements contained in the jQuery object. Listing 5-23 provides a demonstration.

Listing 5-23.  A More Sophisticated Chaining Example

...
<script type="text/javascript">
    $(document).ready(function() {
  
        $("label").css("color", "blue").add("input[name!='rose']")
                    .filter("[for!='snowdrop']").css("font-size", ".75em");
          
        var elems = document.getElementsByTagName("label");
        for (var i = 0; i < elems.length; i++) {
            elems[i].style.color = "blue";
            if (elems[i].getAttribute("for") != "snowdrop") {
                elems[i].style.fontSize= ".75em";
            }
        }
        elems = document.getElementsByTagName("input");
        for (var i = 0; i < elems.length; i++) {
            if (elems[i].getAttribute("name") != "rose") {
                elems[i].style.fontSize= ".75em";
            }
        }
    });
</script>
...

This is an over-the-top example, but it demonstrates the flexibility that jQuery offers. Let’s break down the chained methods to make sense of what is happening. I start with this:

...
$("label").css("color", "blue")
...

I have selected all of the label elements in the document and set the value of the CSS color property to be blue for all of them. The next step is as follows:

...
$("label").css("color", "blue").add("input[name!='rose']")
...

The add method adds the elements that match the specified selector to the jQuery object. In this case, I have selected the input elements that don’t have a name attribute whose value is rose. These are combined with the previously matched elements to give me a jQuery objects that contains a mix of label and input elements. You’ll see more of the add method in Chapter 6. Here is the next addition:

...
$("label").css("color", "blue").add("input[name!='rose']").filter("[for!='snowdrop']")
...

The filter method removes all of the elements in a jQuery object that don’t meet a specified condition. I explain this method in more depth in Chapter 6, but for the moment it is enough to know that this allows me to remove any element from the jQuery object that has a for attribute whose value is snowdrop.

...
$("label").css("color", "blue").add("input[name!='rose']")
    .filter("[for!='snowdrop']").css("font-size", ".75em");
...

The final step is to call the css method again, this time setting the font-size property to .75em. The net result of this is as follows:

  1. All label elements are assigned the value blue for the color CSS property.
  2. All label elements except the one that has the for attribute value of snowdrop are assigned the value .75em for the CSS font-size property.
  3. All input elements that don’t have a name attribute value of rose are assigned the value of .75em for the CSS font-size property.

Achieving the same effect using the basic DOM API is a lot more complex, and I ran into some difficulties while writing this script. For example, I thought I could use the document.querySelectorAll method, described in Chapter 2, to select input elements using the selector input[name!='rose'], but it turns out that this kind of attribute filter doesn’t work with that method. I then tried to avoid duplicating the call to set the font-size value by concatenating the results of two getElementsByTagName calls together, but that turns out to be a painful experience in its own right. I don’t want to labor the point, especially since you must already have a certain commitment to jQuery to be reading this book, but jQuery provides a level of fluidity and expressiveness that is impossible to achieve using the basic DOM API.

Handling Events

Returning to the script I started the chapter with, you can see that I chained together two method calls, as highlighted in Listing 5-24.

Listing 5-24.  Chained Method Calls in the Example Script

...
<script type="text/javascript">
    $(document).ready(function() {
        $("img:odd").mouseenter(function(e) {
           $(this).css("opacity", 0.5);
        }).mouseout(function(e) {
           $(this).css("opacity", 1.0);
        })
    });
</script>
...

The methods I chained were mouseenter and mouseout. These methods let me define handler functions for the mouseenter and mouseout events that I described in Chapter 2. I cover the jQuery support for events in Chapter 9, but I just wanted to show how you can use the behavior of the jQuery object to specify a single handler method for all of the elements that you have selected.

Summary

In this chapter, I introduced you to your first jQuery script and used it to demonstrate some of the key characteristics of the jQuery library: the $ function, the ready event, the jQuery result object, and how jQuery complements, rather than replaces, the built-in DOM API that is part of the HTML specification.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.155.187