We’ve seen how to select document elements based on their tags, ids, classes and attributes. jQuery also provides a number of specialized selectors and filters.
We’ll use the same HTML code as before for our examples. Here it is:
<body> <h2>jQuery Selectors</h2> <table> <tr> <th colspan="2">Comic Summary</th> </tr> <tr class="headerRow"> <th id="comicHeader" class="header cell">Comic</th> <th id="numberHeader" class="header cell">Number</th> </tr> <tr> <td class="cell">Thor</td> <td class="cell">34</td> </tr> <tr> <td class="cell">Superman</td> <td class="cell">158</td> </tr> <tr> <td colspan="2"> Which is best?<br /> <label> <input type="radio" name="dcMarvel" /> DC</label> <label> <input type="radio" name="dcMarvel" /> Marvel</label> </td> </tr> <tr> <td colspan="2"> What are your favourites? <ul> <li><label><input type="checkbox" />Batman</label></li> <li><label><input type="checkbox" />Superboy</label></li> <li><label><input type="checkbox" />Fantastic Four</label></li> <li><input type="checkbox" />Iron Man</li> <li><input type="checkbox" />Incredible Hulk</li> <li><input type="checkbox" />Green Lantern</li> </ul> </td> </tr> <tr> <td colspan="2"> <input type="text" id="jqtest" /></td> </tr> </table> </body>
Selecting by position
There are several ways of selecting elements based on their position in a list. The filters :first and :last (all filters begin with a colon (:)) select the first and last elements in the selected context, respectively. For example, if we wrote $(‘li:first’) we would select the first li element on the page (which is the Batman checkbox). Similarly, $(‘li:last’) selects Green Lantern.
The :first and :last filters ignore the depth of nesting of elements and just pick out the absolute first and last matching elements. Thus $(‘label:first’) gives the DC radio button and $(‘label:last’) gives the Fantastic Four checkbox.
If we omit a selector before the filter, jQuery uses the entire page as the context. Thus $(‘:first’) selects the <html> tag which encloses the whole page, and $(‘:last’) selects the text input tag at the bottom.
The :first-child filter selects the first element in the context that is a child of another element. $(‘label:first-child’) gives the first three labels in the checkbox list because each of these labels is the first child of a li tag. Note that it does not include the DC radio button since, although this button is the first label child of the td tag, it’s not the first child: the br tag has that honour. If we delete the br tag, then the DC button is included; the text ‘Which is best?’ within the td does not count as a child since it just part of the td tag.
As you might expect, $(‘label:last-child’) selects labels that are last children of another node. In this case we get the first three checkbox labels (since they are the only children, they are both first and last children) and the Marvel radio button.
$(‘label:only-child’) selects labels that are the sole child of another node; here that means the first three checkbox labels.
There are also filters :even and :odd which select even and odd elements from the context, using a zero-based index. Thus $(‘li:even’) selects Batman, Fantastic Four and Incredible Hulk and $(‘li:odd’) selects Superboy, Iron Man and Green Lantern.
We have :eq(n), :gt(n), and :lt(n) for finding elements that are at index location n, or greater than or less than n. Again, n is zero-based. Thus $(‘li:gt(2)’) selects Fantastic Four, Incredible Hulk and Green Lantern.
Finally, there is the :nth-child(n) selector, which is a bit unusual, in that its index argument starts at 1 rather than 0. (This is for compatibility with CSS, but if you’re working with jQuery it can be a bit of a pain, since all the other index-based selectors start with a 0 index.) $(‘li:nth-child(2)’) gives Superboy.
There are a couple of other versions of :nth-child(). One takes an argument of ‘even’ or ‘odd’ which returns the even or odd elements in the list, except of course because the list now starts with 1, these give complementary results to using just ‘even’ or ‘odd’ on their own. That is $(‘li:nth-child(odd)’) gives the same result as $(‘li:even’) and vice versa.
The final form of :nth-child() takes a simple formula as its argument. We can write :nth-child(Xn+Y) where X and Y are integers. This will return every Xth element, starting with element Y. Thus $(‘li:nth-child(3n+2)’) returns every 3rd element starting with element 2, giving Superboy and Incredible Hulk.
Custom filters
There are quite a few filters that do their selection based on the type or state of element you’re interested in. Filters that select specific types of element are :button, :checkbox, :file (equivalent to ‘input [type=file]’), :header (selects <h1>…<h6>), :image (same as ‘input [type=image]’), :input (any form input element), :password, :radio, :submit (same as ‘input [type=submit]’), :reset and :text (same as ‘input [type=text]’). These should all be fairly self-explanatory, but give them a try using the HTML above and the header block given earlier, and use the Console in Chrome or Firefox to see what you get.
Somewhat more interesting are the filters that act on the state of an element. The :checked filter selects radio buttons or checkboxes that are checked.
This seems a good place to introduce the tricky :not() filter. As you can guess, it returns the inverse of whatever selector is passed to it as its argument, but you have to be a bit careful. For example, you might think that since :checked gives all radio buttons and checkboxes that checked, :not(:checked) should give you all radio buttons and checkboxes that are not checked. In fact, what it does give you is all elements that are not checked, which means every element on the page (including non-radio buttons and non-checkboxes) except radio buttons and checkboxes that are checked.
If you want just the non-checked checkboxes, you can write $(‘:checkbox:not(:checked)’), since that restricts the context to just checkboxes before applying the :not filter. If for some reason you actually do want both unchecked radio buttons and checkboxes, you can write $(‘:radio:not(:checked), :checkbox:not(:checked)’) being careful not to leave out the comma in the middle.
The :contains() filter can also trap you. Its argument is some text, and it returns all elements that contain that text. The trap lies in the fact that if any element within the context is an ancestor of the element that contains the text, that element also gets included in the results. For example, if we wrote $(‘:contains(best)’), then the td element containing this text would be selected, but so would the html, body, table, tbody and tr elements that are that td’s ancestors. To get just the td element, we would have to write $(‘td:contains(best)’).
If we want to select tag types that have another tag type as their ancestor, we have already seen that we write, for example $(‘tr li’) to find li elements that are children of tr elements. The :has() filter can do the inverse; it finds elements that have a particular type of element as a descendent.
$(‘tr:has(li)’) returns the tr element containing the checkbox list. Again, note that the child element can be at any depth below the parent.
The remaining filters are all fairly obvious. They are :disabled, :enabled, :hidden, :visible, :selected and :parent. The :parent filter returns elements that are parents, not the parents of elements. If you want to find the parents of a set of elements, you need to use the parent() function rather than a filter. Thus $(‘li’).parent() finds the ul element that is the parent of all the li tags.