16 min read

(For more resources related to this topic, see here.)

Understanding the DOM

One of the most powerful aspects of jQuery is its ability to make selecting elements in the DOM easy. The DOM serves as the interface between JavaScript and a web page; it provides a representation of the source HTML as a network of objects rather than as plain text.

This network takes the form of a family tree of elements on the page. When we refer to the relationships that elements have with one another, we use the same terminology that we use when referring to family relationships: parents, children, and so on. A simple example can help us understand how the family tree metaphor applies to a document:

<html> <head> <title>the title</title> </head> <body> <div> <p>This is a paragraph.</p> <p>This is another paragraph.</p> <p>This is yet another paragraph.</p> </div> </body> </html>

Here, <html> is the ancestor of all the other elements; in other words, all the other elements are descendants of <html>. The <head> and <body> elements are not only descendants, but children of <html> as well. Likewise, in addition to being the ancestor of <head> and <body>, <html> is also their parent. The <p> elements are children (and descendants) of <div>, descendants of <body> and <html>, and siblings of each other.

To help visualize the family tree structure of the DOM, we can use a number of software tools, such as the Firebug plugin for Firefox or the Web Inspector in Safari or Chrome.

With this tree of elements at our disposal, we’ll be able to use jQuery to efficiently locate any set of elements on the page. Our tools to achieve this are jQuery selectors and traversal methods.

Using the $() function

The resulting set of elements from jQuery’s selectors and methods is always represented by a jQuery object. Such a jQuery object is very easy to work with when we want to actually do something with the things that we find on a page. We can easily bind events to these objects and add slick effects to them, as well as chain multiple modifications or effects together.

Note that jQuery objects are different from regular DOM elements or node lists, and as such do not necessarily provide the same methods and properties for some tasks.

In order to create a new jQuery object, we use the $() function. This function typically accepts a CSS selector as its sole parameter and serves as a factory returning a new jQuery object pointing to the corresponding elements on the page. Just about anything that can be used in a stylesheet can also be passed as a string to this function, allowing us to apply jQuery methods to the matched set of elements.

Making jQuery play well with other JavaScript libraries

In jQuery, the dollar sign ($) is simply an alias for jQuery. Because a $() function is very common in JavaScript libraries, conflicts could arise if more than one of these libraries were being used in a given page. We can avoid such conflicts by replacing every instance of $ with jQuery in our custom jQuery code.

The three primary building blocks of selectors are tag name, ID, and class. They can be used either on their own or in combination with others. The following simple examples illustrate how these three selectors appear in code:

Selector type

CSS

jQuery

What it does

Tag name

p { }

$(‘p’)

This selects all paragraphs in the document.

ID

#some-id { }

$(‘#some-id’)

This selects the single element in the document that has an ID of some-id.

Class

.some-class { }

$(‘.some-class’)

This selects all elements in the document that have a class of some-class.

When we call methods of a jQuery object, the elements referred by the selector we passed to $() are looped through automatically and implicitly. Therefore, we can usually avoid explicit iteration, such as a for loop, that is so often required in DOM scripting.

Now that we have covered the basics, we’re ready to start exploring some more powerful uses of selectors.

CSS selectors

The jQuery library supports nearly all the selectors included in CSS specifications 1 through 3, as outlined on the World Wide Web Consortium’s site: http://www.w3.org/Style/CSS/specs. This support allows developers to enhance their websites without worrying about which browsers might not understand more advanced selectors, as long as the browsers have JavaScript enabled.

Progressive Enhancement

Responsible jQuery developers should always apply the concepts of progressive enhancement and graceful degradation to their code, ensuring that a page will render as accurately, even if not as beautifully, with JavaScript disabled as it does with JavaScript turned on. We will continue to explore these concepts throughout the article. More information on progressive enhancement can be found at http://en.wikipedia.org/wiki/Progressive_enhancement.

To begin learning how jQuery works with CSS selectors, we’ll use a structure that appears on many websites, often for navigation – the nested unordered list:

<ul id="selected-plays"> <li>Comedies <ul> <li><a href="/asyoulikeit/">As You Like It</a></li> <li>All's Well That Ends Well</li> <li>A Midsummer Night's Dream</li> <li>Twelfth Night</li> </ul> </li> <li>Tragedies <ul> <li><a href="hamlet.pdf">Hamlet</a></li> <li>Macbeth</li> <li>Romeo and Juliet</li> </ul> </li> <li>Histories <ul> <li>Henry IV (<a href="mailto:[email protected]">email</a>) <ul> <li>Part I</li> <li>Part II</li> </ul> <li><a href="http://www.shakespeare.co.uk/henryv.htm">
Henry V</a></li> <li>Richard II</li> </ul> </li> </ul>

Notice that the first <ul> has an ID of selecting-plays, but none of the <li> tags have a class associated with them. Without any styles applied, the list looks like this:

The nested list appears as we would expect it to—a set of bulleted items arranged vertically and indented according to their level.

Styling list-item levels

Let’s suppose that we want the top-level items, and only the top-level items—Comedies, Tragedies, and Histories — to be arranged horizontally. We can start by defining a horizontal class in the stylesheet:

.horizontal { float: left; list-style: none; margin: 10px; }

The horizontal class floats the element to the left-hand side of the one following it, removes the bullet from it if it’s a list item, and adds a 10-pixel margin on all sides of it.

Rather than attaching the horizontal class directly in our HTML, we’ll add it dynamically to the top-level list items only, to demonstrate jQuery’s use of selectors:

$(document).ready(function() { $('#selected-plays > li').addClass('horizontal '); });

Listing 2.1

We begin the jQuery code by calling $(document).ready(), which runs the function passed to it once the DOM has been loaded, but not before.

The second line uses the child combinator (>) to add the horizontal class to all the top-level items only. In effect, the selector inside the $() function is saying, “Find each list item (li) that is a child (>) of the element with an ID of selected-plays (#selected-plays)”.

With the class now applied, the rules defined for that class in the stylesheet take effect, which in this case means that the list items are arranged horizontally rather than vertically. Now our nested list looks like this:

Styling all the other items—those that are not in the top level—can be done in a number of ways. Since we have already applied the horizontal class to the top-level items, one way to select all sub-level items is to use a negation pseudo-class to identify all list items that do not have a class of horizontal. Note the addition of the third line of code:

$(document).ready(function() { $('#selected-plays > li').addClass('horizontal'); $('#selected-plays li:not(.horizontal)').addClass('sub- level');li:not(.horizontal)').addClass('sub-level'); });

Listing 2.2

This time we are selecting every list item (<li>) that:

  • Is a descendant of the element with an ID of selected-plays (#selected-plays)
  • Does not have a class of horizontal (:not(.horizontal))

When we add the sub-level class to these items, they receive the shaded background defined in the stylesheet:

.sub-level { background: #ccc; }

Now the nested list looks like this:

Attribute selectors

Attribute selectors are a particularly helpful subset of CSS selectors. They allow us to specify an element by one of its HTML attributes, such as a link’s title attribute or an image’s alt attribute. For example, to select all images that have an alt attribute, we write the following:

$('img[alt]')

Styling links

Attribute selectors accept a wildcard syntax inspired by regular expressions for identifying the value at the beginning (^) or end ($) of a string. They can also take an asterisk (*) to indicate the value at an arbitrary position within a string or an exclamation mark (!) to indicate a negated value.

Let’s say we want to have different styles for different types of links. We first define the styles in our stylesheet:

a { color: #00c; } a.mailto { background: url(images/email.png) no-repeat right top; padding-right: 18px; } a.pdflink { background: url(images/pdf.png) no-repeat right top; padding-right: 18px; } a.henrylink { background-color: #fff; padding: 2px; border: 1px solid #000; }

Then, we add the three classes—mailto, pdflink, and henrylink—to the appropriate links using jQuery.

To add a class for all e-mail links, we construct a selector that looks for all anchor elements (a) with an href attribute ([href]) that begins with mailto: (^=”mailto:”), as follows:

$(document).ready(function() { $('a[href^="mailto:"]').addClass('mailto'); });

Listing 2.3

Because of the rules defined in the page’s stylesheet, an envelope image appears after the mailto: link on the page.

To add a class for all the links to PDF files, we use the dollar sign rather than the caret symbol. This is because we’re selecting links with an href attribute that ends with .pdf:

$(document).ready(function() { $('a[href^="mailto:"]').addClass('mailto'); $('a[href$=".pdf"]').addClass('pdflink'); });

Listing 2.4

The stylesheet rule for the newly added pdflink class causes an Adobe Acrobat icon to appear after each link to a PDF document, as shown in the following screenshot:

Attribute selectors can be combined as well. We can, for example, add the class henrylink to all links with an href value that both starts with http and contains henry anywhere:

$(document).ready(function() { $('a[href^="mailto:"]').addClass('mailto'); $('a[href$=".pdf"]').addClass('pdflink'); $('a[href^="http"][href*="henry"]') .addClass('henrylink'); }); });

Listing 2.5

With the three classes applied to the three types of links, we should see the following:

Note the PDF icon to the right-hand side of the Hamlet link, the envelope icon next to the email link, and the white background and black border around the Henry V link.

Custom selectors

To the wide variety of CSS selectors, jQuery adds its own custom selectors. These custom selectors enhance the already impressive capabilities of CSS selectors to locate page elements in new ways.

Performance note

When possible, jQuery uses the native DOM selector engine of the browser to find elements. This extremely fast approach is not possible when custom jQuery selectors are used. For this reason, it is recommended to avoid frequent use of custom selectors when a native option is available and performance is very important.

Most of the custom selectors allow us to choose one or more elements from a collection of elements that we have already found. The custom selector syntax is the same as the CSS pseudo-class syntax, where the selector starts with a colon (:). For example, to select the second item from a set of <div> elements with a class of horizontal, we write this:

$('div.horizontal:eq(1)')

Note that :eq(1) selects the second item in the set because JavaScript array numbering is zero-based, meaning that it starts with zero. In contrast, CSS is one-based, so a CSS selector such as $(‘div:nth-child(1)’) would select all div selectors that are the first child of their parent. Because it can be difficult to remember which selectors are zero-based and which are one-based, we should consult the jQuery API documentation at http://api.jquery.com/category/selectors/ when in doubt.

Styling alternate rows

Two very useful custom selectors in the jQuery library are :odd and :even. Let’s take a look at how we can use one of them for basic table striping given the following tables:

<h2>Shakespeare's Plays</h2> <table> <tr> <td>As You Like It</td> <td>Comedy</td> <td></td> </tr> <tr> <td>All's Well that Ends Well</td> <td>Comedy</td> <td>1601</td> </tr> <tr> <td>Hamlet</td> <td>Tragedy</td> <td>1604</td> </tr> <tr> <td>Macbeth</td> <td>Tragedy</td> <td>1606</td> </tr> <tr> <td>Romeo and Juliet</td> <td>Tragedy</td> <td>1595</td> </tr> <tr> <td>Henry IV, Part I</td> <td>History</td> <td>1596</td> </tr> <tr> <td>Henry V</td> <td>History</td> <td>1599</td> </tr> </table> <h2>Shakespeare's Sonnets</h2> <table> <tr> <td>The Fair Youth</td> <td>1–126</td> </tr> <tr> <td>The Dark Lady</td> <td>127–152</td> </tr> <tr> <td>The Rival Poet</td> <td>78–86</td> </tr> </table>

With minimal styles applied from our stylesheet, these headings and tables appear quite plain. The table has a solid white background, with no styling separating one row from the next, as shown in the following screenshot:

Now we can add a style to the stylesheet for all the table rows and use an alt class for the odd rows:

tr { background-color: #fff; } .alt { background-color: #ccc; }

Finally, we write our jQuery code, attaching the class to the odd-numbered table rows (<tr> tags):

$(document).ready(function() { $('tr:even').addClass('alt'); });

Listing 2.6

But wait! Why use the :even selector for odd-numbered rows? Well, just as with the :eq() selector, the :even and :odd selectors use JavaScript’s native zero-based numbering. Therefore, the first row counts as zero (even) and the second row counts as one (odd), and so on. With this in mind, we can expect our simple bit of code to produce tables that look like this:

Note that for the second table, this result may not be what we intend. Since the last row in the Plays table has the alternate gray background, the first row in the Sonnets table has the plain white background. One way to avoid this type of problem is to use the :nth-child() selector instead, which counts an element’s position relative to its parent element rather than relative to all the elements selected so far. This selector can take a number, odd, or even as its argument:

$(document).ready(function() { $('tr:nth-child(odd)').addClass('alt'); });

Listing 2.7

As before, note that :nth-child() is the only jQuery selector that is one-based. To achieve the same row striping as we did earlier—except with consistent behavior for the second table—we need to use odd rather than even as the argument. With this selector in place, both tables are now striped nicely, as shown in the following screenshot:

Finding elements based on textual content

For one final custom-selector touch, let’s suppose for some reason we want to highlight any table cell that referred to one of the Henry plays. All we have to do—after adding a class to the stylesheet to make the text bold and italicized ( .highlight {font-weight:bold; font-style: italic;} )—is add a line to our jQuery code using the :contains() selector:

$(document).ready(function() { $('tr:nth-child(odd)').addClass('alt'); $('td:contains(Henry)').addClass('highlight'); });

Listing 2.8

So, now we can see our lovely striped table with the Henry plays prominently featured:

It’s important to note that the :contains() selector is case sensitive. Using $(‘td:contains(henry)’) instead, without the uppercase “H”, would select no cells.

Admittedly, there are ways to achieve the row striping and text highlighting without jQuery—or any client-side programming, for that matter. Nevertheless, jQuery, along with CSS, is a great alternative for this type of styling in cases where the content is generated dynamically and we don’t have access to either the HTML or server-side code.

Form selectors

The capabilities of custom selectors are not limited to locating elements based on their position. For example, when working with forms, jQuery’s custom selectors and complementary CSS3 selectors can make short work of selecting just the elements we need. The following table describes a handful of these form selectors:

Selector

Match

:Input

Input, text area, select, and button elements

:button

Button elements and input elements with a type attribute equal to button

:enabled

Form elements that are enabled

:disabled

Form elements that are disabled

:checked

Radio buttons or checkboxes that are checked

:selected

Option elements that are selected

As with the other selectors, form selectors can be combined for greater specificity. We can, for example, select all checked radio buttons (but not checkboxes) with $(‘input[type=”radio”]:checked’) or select all password inputs and disabled text inputs with $(‘input[type=”password”], input[type=”text”]:disabled’). Even with custom selectors, we can use the same basic principles of CSS to build the list of matched elements.

Summary

With the techniques that we have covered in this article, we should now be able to locate sets of elements on the page in a variety of ways. In particular, we learned how to style top-level and sub-level items in a nested list by using basic CSS selectors, how to apply different styles to different types of links by using attribute selectors, add rudimentary striping to a table by using either the custom jQuery selectors :odd and :even or the advanced CSS selector :nth-child(), and highlight text within certain table cells by chaining jQuery methods.

Resources for Article:


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here