Tripping the DOM fantastic with jQuery
Jay Blanchard, author of the book Applied jQuery, explains how to work with elements in your markup that are either ancestors or descendants of an HTML element that you have selected. Here he focuses on the DOM tree traversal methods
Gene Kelly I ain’t.
Don’t get me wrong, I can dance. But it’s certainly not the thing of beauty you see in the old Hollywood musicals – unless you’re watching me dance through the DOM (the Document Object Model) with jQuery.
There are many times when you need to be able to work with elements in your markup that are either ancestors (parents, grandparents, etc) or descendants (children, grandchildren, etc) of an HTML element that you have selected. The concept is that you’re moving up, down and sideways in the family tree, the DOM, from the limb (the current element) on which you’re standing. This movement is known as traversing the DOM.
jQuery offers several categories of methods that will allow you to traverse the DOM with ease and grace to get to the elements that you need to work with when coding your frontend interfaces. The jQuery library breaks up the library like this:
- Miscellaneous traversing methods include add(), andSelf(), contents(), and end(): api.jquery.com/category/traversing/miscellaneous-traversal/
- Filtering methods are comprised of eq(), filter(), first(), has(), is(), last(), map(), not() and slice(): api.jquery.com/category/traversing/filtering/
- Tree traversal, which is the largest category of traversal methods. Home to 14 functions including children(), closest(), find(), next(), nextAll(), nextUntil(), offsetParent(), parent(), parents(), parentsUntil(), prev(), prevAll(), prevUntil() and siblings(): api.jquery.com/category/traversing/tree-traversal/
For this article I will focus on the tree traversal methods.
Anatomy of a DOM tree
The DOM ensures that everything is placed within a tree-like hierarchy so that all of your virtual limb-to-limb movement is easy to visualise and to perform. Have a look at the following illustration to see an example of the branches that might be on your DOM tree.
The branches should look pretty familiar to you. There is a div that contains an unordered list, another div that contains more divs, a table and another div containing paragraphs and images. Let’s start putting jQuery’s tree traversal methods to work to move around this tree.
Get top Black Friday deals sent straight to your inbox: Sign up now!
We curate the best offers on creative kit and give our expert recommendations to save you time this Black Friday. Upgrade your setup for less with Creative Bloq.
Up and down we go
With no further markup, including ids or classes, and throwing in a couple of the other filter methods you can traverse the tree quite easily.
(NOTE: all examples were tested at jsfiddle.net).
The markup for the unordered list looks like this:
<div> <ul> <li><a href="#">one</a></li> <li><a href="#">two</a></li> <li><a href="#">three</a></li> </ul></div>
For this exercise the goal is to click a link in the unordered list and change the background colour of all of the ancestors. Here is the jQuery code to perform that action:
$('a').click(function(e){ if(0 == $(this).parent().index()){ $(this).parents().css('background-color', '#CCFFCC'); }else if(1 == $(this).parent().index()){ $(this).parents().css('background-color', '#99CC99'); }else if(2 == $(this).parent().index()){ $(this).parents().css('background-color', '#669966'); }});
Since you want each link to have a specific action, you need to determine where it is in relation to the rest of the tree. Each anchor tag is a direct descendant of a list item, so the anchor tag in this case will always have an index value of 0. That index value does not uniquely identify the link in this case but the direct ancestor, the parent, of the link is a member of a group of list items. The DOM makes sure that each of these list items has a unique index identity as descendants of the unordered list element. Look at the annotated markup:
<div> <ul> <li><a href="#">one</a></li> // li is index 0 of ul <li><a href="#">two</a></li> // li is index 1 of ul <li><a href="#">three</a></li> // li is index 2 of ul </ul></div>
Using $(this).parent().index() causes the anchor tag to locate its parent and then determine the parent’s index value.
Next you want to apply some CSS properties to all of the ancestors of the anchor tag. By simply specifying the parents() method, jQuery will apply the change as called for. Head over to jsfiddle.net/jayblanchard/8PqvJ/ and check out the results.
You’ll no doubt notice that things did not necessarily go as you expected. If you clicked all three links in order from top to bottom you got the end result as shown in the next illustration.
In order to understand what occurred all you have to do is look at all of the ancestors of the anchor tag, which are the list item, the unordered list, the div, the body and the HTML elements. Without defining a selector for the parents() method you get all of the parents. Clicking on the next indexed item only changes the background colour of the parents of that anchor tag and this behaviour continues as each of the links are clicked. Since the other anchor tags and list items are not in the parents chain they are not changed.
You can limit the climb up the ancestor tree using jQuery’s parentsUntil() method. Change the jQuery code from the previous example to the following:
$('a').click(function(e){ if(0 == $(this).parent().index()){ $(this).parentsUntil('ul').css('background-color', '#CCFFCC'); }else if(1 == $(this).parent().index()){ $(this).parentsUntil('ul').css('background-color', '#99CC99'); }else if(2 == $(this).parent().index()){ $(this).parentsUntil('ul').css('background-color', '#669966'); }});
The CSS is applied only to the direct parent (yes – I know there is a much more succinct way to do this!) of the clicked element. You can see this in action here: jsfiddle.net/jayblanchard/uTakJ/.
Another way to find a particular ancestor is to use jQuery’s closest() method. The closest() method begins at the currently selected element and works its way up the ancestor chain until it finds the first item matching the selector.
$('a').click(function(e){ e.preventDefault(); $(this).closest('li').css('background-color', '#999966');});
Still using the unordered list created earlier, the closest() method used here will start with the anchor tag and search up the ancestor tree until it finds a list item.
What about descendants? They’re just as easy to get to with jQuery’s traversal methods. The following markup is applied to the table from our tree:
<table> <tr> <td>Row 1 Cell 1</td> <td>Row 1 Cell 2</td> </tr> <tr> <td>Row 2 Cell 1</td> <td>Row 2 Cell 2</td> </tr></table>
If you want all of the children of the table, the rows (tr) to have a style applied to them you select them like this:
$('table').children().css('background-color', '#FFFF00');
The children are always one-level down from the parent element, the immediate children. If you want to work with descendants further down the line you should use the find() method. For instance if you want to change the background colour of last cells in each row you could write the following:
$('tr').find('td:last').css('background-color', '#FFFF00');
The find() method was used to locate all table cells and the jQuery filter :last was used to make sure that only the last cell in row had the CSS applied to it. Changing the selector from table row (tr) to table yields an entirely different result as seen in the next illustration:
Now that you can find ancestor and descendant elements, let’s turn our focus to finding those elements next to each other.
Brothers and sisters
jQuery exposes a couple of methods for working with the brothers and sisters of DOM elements. The most commonly used of these methods is the siblings() method. Using this method will identify all of the siblings of a selected element. For this example I expanded the unordered list and used the following jQuery to change the background colour of the siblings in question:
$('a').click(function(e){ e.preventDefault(); $(this).parent().siblings().css('background-color', '#666699');});
The anchor tag has no siblings but its parent element, the list item, does. Clicking on the link containing the word ‘two’ yields the following result:
Each of the siblings of the list item have had their CSS modified.
When working with an element’s siblings you can think of them just as you would any group of brothers and sisters. Some children (elements) are older than others.
If you want to select siblings that come before the current child, the older children, you can use the prev(), prevAll() and prevUntil() jQuery methods. As you might imagine, the prev() method chooses the sibling that immediately precedes the selected child. The prevAll() method encompasses all of the older children. You also have the ability to choose a group of older children up to a certain point (just as you did with parentsUntil()) with prevUntil().
Siblings younger than the selected child element can be defined with the next(), nextAll() and nextUntil().
More on jQuery’s traversal methods
I encourage you to have a look at the other traversal methods, including some filters that are very useful, offered by the jQuery library. You can find the documentation at api.jquery.com/category/traversing/.
Thank you for reading 5 articles this month* Join now for unlimited access
Enjoy your first month for just £1 / $1 / €1
*Read 5 free articles per month without a subscription
Join now for unlimited access
Try first month for just £1 / $1 / €1
The Creative Bloq team is made up of a group of design fans, and has changed and evolved since Creative Bloq began back in 2012. The current website team consists of eight full-time members of staff: Editor Georgia Coggan, Deputy Editor Rosie Hilder, Ecommerce Editor Beren Neale, Senior News Editor Daniel Piper, Editor, Digital Art and 3D Ian Dean, Tech Reviews Editor Erlingur Einarsson and Ecommerce Writer Beth Nicholls and Staff Writer Natalie Fear, as well as a roster of freelancers from around the world. The 3D World and ImagineFX magazine teams also pitch in, ensuring that content from 3D World and ImagineFX is represented on Creative Bloq.