Remote Synthesis
Search my blog:
Viewing By Entry / Main
Sep 28, 2009

Advanced jQuery Selectors & Traversing - Part 1

This is the first in a series of posts that will hopefully offer some solid skill-building for using selectors and traversing through the DOM with jQuery. Most examples I end up seeing use a lot of id-based (i.e. $("#mydiv")) or class-based (i.e. $(".myClass")) selectors. While there is nothing wrong with this, jQuery offers a lot of power for traversing the DOM beyond this. Also, it removes the useless classes or ID's you might litter your page with simply to work within jQuery.

The first part of this series will cover some slightly advanced selector and traversing options such as combining selectors, adding filters and using selectors in conjunction with traversing methods. We'll move on to more advanced techniques (and a really neat sample application) in future posts in this series.

Combining Selectors

To keep things simple for our example I have created a very basic HTML page that displays a simple page navigation and sub-navigation both as unordered lists as would be a common practice. In many other examples, you might find that each unordered list and even each navigation item within the lists would be given an ID that could be referenced by jQuery. However, this example only has an ID on the navigation div and nothing else. What I intend to show is that you can access these navigational elements and even traverse through them without the need for these extraneous ID's.

<html>
<head>
<title>jQuery Examples</title>
   <script src="scripts/jquery-1.3.2.min.js"></script>
</head>
<body>
<div id="navigation">
   <ul>
      <li><a href="index.cfm">Home</a></li>
      <li><a href="index.cfm?navItem=Page2">Page 2</a></li>
      <li><a href="index.cfm?navItem=Page3">Page 3</a></li>
      <li><a href="index.cfm?navItem=ContactUs">Contact Us</a></li>
   </ul>
   <!--- subnav --->
   <ul>
      <li><a href="index.cfm?subItem=Sub1">Sub 1</a></li>
      <li><a href="index.cfm?subItem=Sub2">Sub 2</a></li>
   </ul>
</div>
</body>
</html>

Let's begin by taking a very simple use-case. In this scenario I want an array of all of my navigational elements, regardless of whether they are within the primary or sub-navigation. In this case, I simply combine my selectors to select all <li> elements within the navigational div:

$("#navigation li")

Obviously, you can keep combining selectors to your hearts content. For instance, the following code will expand the above to select specifically the <a> tags within the <li> navigational elements:

$("#navigation li a")

Adding Filters

Combining selectors is great except what if we want to get a specific navigational element, even in this simple example. I could get the entire array and then select a specific element by its place in the array like the following example that gets the first item in the navigation:

navItems = $("#navigation li")
navItems[0] // first navigational item

However, this can become unreliable and complicated depending on what item I want to get. Thankfully, jQuery offers filters that you can append to your selectors that will help you refine your selections. For instance, here's an example of getting the same navigation item above by using the ":first" filter:

$("#navigation ul:first li:first")

You could alternatively use the eq() filter to select a specific item index which would be functionally equivalent to the array example above but using purely selector syntax:

$("#navigation ul:first li:eq(1)")

You can also combine any of these selectors with attribute filters that can further refine your search based upon the value of a specific tag attribute. For example, the following example selects the <a> tag of the first navigational element based upon adding the attribute filter looking for the contents of the href attribute (this might be handy, for instance, for setting a style on navigational elements matching the current URL):

$("#navigation ul:first li a[href='index.cfm']")

Obviously, this is but a small subset of the actual possibilities available with the full set of selectors and filters. However, as you can see, your selector/filter code can get pretty complex if it needs to. However, sometimes the desired result is more easily (or only) achieved using some DOM traversing techniques.

Combining Selectors, Filters and Traversing

There is a long list of methods for traversing the DOM using jQuery and some actually duplicate functionality you might be able to achieve with selectors and filters (ex. eq() and not()). Still, using a combination of selectors, filters and traversing, offers a powerful set of tools to access any single object or group of objects in the DOM.

Let's look at an example. Let's say, I wanted to access the first navigational element in the sub-navigation from our example page. I could use just selectors and filters as below:

$("#navigation ul:last li:first")

However, I could also use the children() traversing method to get all the children of my sub-navigation unordered list and filter those children to only the first item, as below:

$("#navigation ul:last").children(":first")

In a similar example, let's say I wanted only the child (or children) that contain a specific text string. I can do this by combining a children() method refined with the contains() filter method.

$("#navigation ul:last").children(":contains('Sub 2')")

Let's look at a more complicated example that combines a number of filters, selectors and traversal methods. In this example we get the next two items after the first primary navigation item and then filter it to see if one contains the number 2. There are, I believe, easier ways to access these specific items but this shows how complex your selectors and traversal method chaining combinations can get.

$("#navigation ul:first").children(":first").siblings().slice(0,1).filter(":contains('2')")

Using Variables Within Selectors

Let's add in one more element to our tutorial here: variables. In doing so, I want to expand on our example page adding in an useful function that will add a class to the navigation item that represents the current page (the class simply bolds the item) and also removes the link from this item (no use for the page to link back to itself). All of this will be done simply by adding a variable into our selector logic and using some basic traversing and other jQuery methods and, more importantly, without modifying the underlying HTML layout at all (you can see the page at work here).

Below is our jQuery/JavaScript code which runs when the page is ready using an anonymous function:

$(document).ready(function () {
   var currentURL = "index.cfm";
   // get the current page by parsing the url
   urlParts = location.href.split("/");
   if (urlParts[urlParts.length - 1].length) { // if the end of the url isn't just a slash
      currentURL = urlParts[urlParts.length - 1];
   }
   // select the li parent of the matching a tag
   selectedPage = $("#navigation ul li a[href='" + currentURL + "']").parent();
   $(selectedPage).addClass("currentPage");
   $(selectedPage).html($(selectedPage).children("a").html()); // remove the a tag altogether
});

The first six lines are simple for parsing the current file plus query string from the URL - and defaulting it to simply index.cfm if its empty. This is done using simple JavaScript, not specifically anything jQuery.

The next section actually takes that string variable and injects it into a selector in order to get the list item that contains an <a> tag with the matching href. Finally, we add a class to that matching <li> item (which, as I mentioned, just bolds it) and remove the <a> tag by inserting the <a> tags contents into the parent <li> tag.

A More Advanced Example - Tic-Tac-Toe

In developing this tutorial (and after seeing Ray Camden's Hangman game built with jQuery), I came up with the idea that the logic of a game of tic-tac-toe could mostly be done using jQuery selectors and traversing methods which will serve as the basis for the next part(s) of this tutorial series. I built a prototype of this game, which you can see here (please note, there is a bug I will discuss in the post that occurs seemingly randomly). You may call this "a strange game" and claim that "the only winning move is not to play." Nonetheless, it serves as a great example of the power of jQuery selectors, filters and traversal methods.

Comments
Bill
Nice article. I think I encountered your strange tic-tac-toe bug (screen cleared).

Also, I assume you didn't really mean for the current tic-tac-toe example to be a real playable version as I never lose (i.e. immature AI) and that it is just a proof of concept?


Brian Rinaldi
@bill - thanks. Yeah, that is the bug. It actually seems to be something internal to jQuery to be honest but I can't completely determine the source just yet. Perhaps by the time I get Part 2 out I will have figured it out.

Yeah, its a proof of concept really (more a learning example) and not intended to be something challenging. The AI is basically, it places an O in a random open spot. I thought an update to it could include the option for a higher difficulty...assuming I actually continue to develop it.


Write your comment



(it will not be displayed)