Understanding Basic DOM Traversal in jQuery

November 4, 2014

Traversing the DOM using jQuery tends to be most useful when you've got the same sections of code, or modules, repeating on a page. And, more importantly, the same action can occur in those modules that needs to be specific to the module where it happens. Let's face it, if the action only happens in one spot to specific elements we're probably fine attaching specific IDs to those elements and targeting our jQuery at those ID's. However, these repeating, actionable portions of code is where I find DOM traversal to be the most useful in order to keep jQuery code succint. Here are some useful DOM traversal methods and examples to get you started.

.parent()

In HTML, the parent element refers to the immediate element that is wrapped around a given element, which is also its closest “ancestor”. The .parent() method will get the parent element of the matched set. You can also filter by a selector. I.e. .parent(“.some-class”).

Let’s take this HTML:

<div class="alert">
  <span class="close-button">X</span>
  <p>Great job!</p>
</div>
<div class="alert">
  <span class="close-button">X</span>
  <p>Hmmm. There seems to be an error.</p>
</div>

So we have two divs that are serving as alert fields and I want the user to be able to click the X (the span with a class of “close-button”) to close an alert after they’ve acknowledged it.

$(".close-button").on("click",function(){
  $(this).parent(".alert").hide();
});

So with this code no matter how many alerts their are on the page when we click the X to close we are only hiding the parent .alert of the .close-button we clicked and no other. Here’s a live demo:

See the Pen parent by Rich (@richfinelli) on CodePen.

So “this” is referring to the span (close-button) that we clicked on. And NOT the close-button element in the other alert that we didn’t click on. So using the .parent() method we were allowed to get to the parent of the element we clicked on. We also filtered it using the class “alert” which made it specific to only the parent with that specific class.

.siblings()

An elements sibling or siblings are elements that do not appear inside that element nor are they wrapped around that element. Sibling elements are on the same html level as each other. Take this code:

  <h2>Sibling of div</h2> 
  <div>Sibling of h2</div>

The div isn’t wrapped around the h2 and the h2 ain’t wrapped around the div. They are on the same level in the HTML and are thus siblings. So let’s say on a side bar of a page we have multiple expandable containers. Each container can be expanded to show more information. The html looks like this:

<ul> 
  <li> 
    <h2>Container 1 <a class="expand" href="">expand</a></h2> 
    <div class="container"> 
      <p>This is an expanded container</p> 
      <a class="close" href="#">close</a> 
    </div> 
  </li> 
  <li> 
    <h2>Container 2 <a class="expand" href="">expand</a></h2> 
    <div class="container"> 
      <p>This is an expanded container</p> 
      <a class="close" href="#">close</a> 
    </div> 
  </li> 
  <li> 
    <h2>Container 3 <a class="expand" href="">expand</a></h2> 
    <div class="container"> 
      <p>This is an expanded container</p> 
      <a class="close" href="#">close</a> 
    </div> 
  </li>
</ul>

It’s three list items that each have an h2 that is the always-visible portion of the container, with a link to expand the container. Underneath is the div that is the collapsable portion of the container. The sibling method will be really helpful here to help us get from the anchor to the container. What we need to do is actually go from the anchor (where the user will click), to it’s parent, the h2, to it’s sibling the div. Here is the jQuery to do that.

$(".expand").on("click",function(event){ 
  event.preventDefault(); 
  $(this).fadeOut().parent().siblings(".container").slideDown(); 
}); 

The event.preventDefault(); just makes it so the anchor tag doesn’t take us to the top of the page. Then, I’m actually chaining a few things together. First we start with “this” which refers to the anchor tag. I want that to disapear gradually, so the .fadeOut() method handles that. Then we start our DOM travels. We go to the parent (which is the h2), then we go to the h2’s siblings, specifically one with the class of “container”. Finally, now that we are targetting “container’ we tell it to slide down. Here’s a demo:

See the Pen siblings by Rich (@richfinelli) on CodePen.

.children()

A child element is the element inside of another element. So the main thing missing from our expandable / collapsible containers was that we didn’t have a way to collapse the container. We need something to close the container, so in the example below I’ve expanded on the last one to include a close link inside the container. So when clicking on the link to close we’ll have to travel to the container to close it. Nothing new there we can use parent for that. But, the expand link fades out on expand, so it should fade back in on collapse. So to do that we have to go from the close button to the container to the h2 to the expand link. So that’s parent, sibling, child. Here’s the live demo:

See the Pen children by Rich (@richfinelli) on CodePen.

.closest()

Not as obvious as it seems like it should be. This is one of the reasons I wanted to write this post. So I could clearly understand the difference between the .closest() and the .find() method so I didn’t have to look it up every time I needed one of them. And if I forget at least I have an easy place to find it. .closest() is similar to parent as it traverses up thru the DOM tree. Unlike .parent(), .closest() doesn’t look at just the immediate parent. It goes to grandpa the great grandpa, etc until it finds what it’s looking for. It needs to get filtered by a selector. You would think that based on its name that .closest() would look up or down the DOM tree just to find the closest matching set, yet it doesn’t. It only goes up. Here’s a live demo.

See the Pen closest by Rich (@richfinelli) on CodePen.

.find()

.find() is similar to .closest() except it searches down the DOM tree instead of up the DOM tree. It’s also very similar to .children() as it goes down the DOM tree to find the matching set, however, .find() will look not only at child elements, but also at grand children, great grand children, and keep going through all descendants until it finds what it’s looking for. Here’s the HTML:

<section>
  <button>Save</button> 
  <div> 
    <h1>Headline</h1> 
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Labore dolore nihil, sit nostrum et explicabo minima rem veritatis voluptates mollitia!<span class="save-holder"></span></p> 
  </div> 
</section>

I want to go from the button to it’s sibling div, to its child p, to it’s child .save-holder. So I could do that like this:

$("button").on("click",function(){ 
  $(this).siblings().children().children(".save-holder").text("Post Saved"); 
});

I’m just doubling up the .children() method in order to look for the children of children. Or, I could make things a little simpler and use .find():

  $("button").on("click",function(){ 
    $(this).siblings().find(".save-holder").text("Post Saved"); 
  });
  

.find() will look through all descendants without stopping while .children() only looks at the first immediate descendant (aka the children).

See the Pen find by Rich (@richfinelli) on CodePen.

Conclusion

The recurring theme in these examples are actions that occur that affect other elements in the DOM. Those elements that are being affected aren’t named directly by an ID or class, but instead they traverse the DOM starting from the keyword, this, which is typically the element that was clicked on. One of the downsides of DOM traversal is if the DOM has the potential to change then DOM traversal can be affected. However if the DOM is relatively stable, traversing the DOM is especially effective when certain modules repeat and do the same actions that only affect the module where the action is happening. Traversing the DOM can be very useful in these situations to help make it so your code doesn’t repeat iteself.

Mastering CSS: Book by Rich Finelli
Mastering CSS, the Book! Flexbox, Layout, Animations, Responsive, Retina, and more!
Mastering CSS, 2nd Edition, video course by Rich Finelli
Mastering CSS, Second Edition: 47 videos on how to make websites like a boss! Flexbox, Animations, Responsive, Retina, and more!
  • Rich

    I continue to return to this article again and again to remind myself the difference between .closest() and .find(). closest goes UP the dom tree to look at ancestors while find searches DOWN the dom to look through descendants. 🙂

Back to top