jQuery's Closest() Method Returns Only One Ancestor

Posted October 6, 2009 at 9:31 AM by Ben Nadel

Tags: Javascript / DHTML

If you need to get an anscestor of a given element, jQuery's parents() method is really useful. Not only will it crawl up through the DOM tree, it allows you to supply a jQuery selector that can filter the collection of parent nodes that is returned. This works in most cases; however, when the anscestor or parent that you are looking for depends on the context of the given element, working with the parents() traversal method might not do what you need. This is where the Closest() method can really shine. New in jQuery 1.3, the Closest() method starts with the given element, and then moves up the node tree looking for the first (and only the first) anscestor that matches the given selector.

NOTE: If the given node matches the selector, it is returned as the "closest" element and no upward traversal is performed.

 
 
 
 
 
 
 
 
 
 

To experiment with this, I have created a very simple HTML page that has two text input boxes. One of the boxes has both a DIV and a P anscestor while the other has only a DIV anscestor. When the user blurs (moves away from) a given text input box, we want to find the closest anscestor - P or DIV - and assign it the CSS class, "filled":

  • <!DOCTYPE HTML>
  • <html>
  • <head>
  • <title>jQuery Closest() Method</title>
  • <style type="text/css">
  •  
  • div,
  • p {
  • border: 1px solid #E0E0E0 ;
  • margin: 0px 0px 0px 0px ;
  • padding: 20px 20px 20px 20px ;
  • }
  •  
  • div.filled {
  • background-color: #F0F0F0 ;
  • }
  •  
  • p.filled {
  • background-color: #FFECEC ;
  • }
  •  
  • </style>
  • <script type="text/javascript" src="jquery-1.3.2.js"></script>
  • <script type="text/javascript">
  •  
  • // When the DOM is ready, initialize scripts.
  • $(function(){
  •  
  • // Hook up the blur event for the input boxes.
  • $( "input" ).blur(
  • function(){
  •  
  • // When the input is blurred, we want to find
  • // the closest parent that is a P or a DIV and
  • // then add the CSS class, "filled" to it.
  • $( this )
  • .closest( "p, div" )
  • .addClass( "filled" )
  • ;
  •  
  • }
  • );
  •  
  • });
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <h1>
  • jQuery Closest() Method
  • </h1>
  •  
  • <form>
  •  
  • <!--- DIV and P. --->
  • <div>
  • <p>
  • <span>
  • <input type="text" />
  • </span>
  • </p>
  • </div>
  •  
  • <br />
  •  
  • <!--- DIV only. --->
  • <div>
  • <span>
  • <input type="text" />
  • </span>
  • </div>
  •  
  • </form>
  •  
  • </body>
  • </html>

As you can see above, when the input text box is blurred, we find the most closest parent using jQuery's Closest() method:

  • $( this ).closest( "p, div" ).addClass( "filled" );

Notice that we can give the Closest() traversal method several different selectors; this allows us to search for multiple types of anscestor but still stop searching once we've reached the closest one to the given element. And, when we run the above page and blur both input boxes, this is what we get:

 
 
 
 
 
 
jQuery's Closest() Method Finds The Closest Ancestor In The Node Tree (Including The Given Node). 
 
 
 

Notice that in the first case, only the P anscestor was given the "filled" class despite the fact that its parent node tree contains both P and DIV tags. Then, in the second case, only the DIV anscestor was given the "filled" class. The beauty here is that the Closest() method allows us to select the most appropriate container even when the most appropriate container changes depending on the context of the given node.

The Closest() method is very cool and very useful, especially when you start to deal with event delegation in which events are trapped at a higher level in the node tree than are the elements that triggered the event. But this got me thinking about events and event bubbling; and I wondered, as an experiment based on the above demonstration, could we accomplish the same thing using event bubbling?

 
 
 
 
 
 
 
 
 
 

In this next demonstration, all I'm going to do is change the jQuery code. Rather than having the input-blur event explicitly find the closest anscestor, we're simply going to have it trigger a custom event. jQuery will then take this custom event and start to bubble it up through the DOM node tree. To leverage that event bubbling to our advantage, we're going to bind the P and DIV tags to this custom event such that they will listen for it as it bubbles up through the node tree:

NOTE: I am only showing the jQuery updates as the HTML has not changed from above.

  • <script type="text/javascript">
  •  
  • // When the DOM is ready, initialize scripts.
  • $(function(){
  •  
  • // Find all DIV and P tags and bind to the input blur
  • // custom event.
  • $( "p, div" ).bind(
  • "inputBlur",
  • function(){
  • // Add the filled class to the given element.
  • $( this ).addClass( "filled" );
  •  
  • // Cancel the event bubbling. This way, even
  • // if the given collection contains other
  • // nodes higher up, the events will never
  • // bubble up to them.
  • return( false );
  • }
  • );
  •  
  • // Hook up the blur event for the input boxes.
  • $( "input" ).blur(
  • function(){
  • // Trigger our blur custom event on the input
  • // box so that it will bubble up through the
  • // node tree.
  • $( this ).trigger( "inputBlur" );
  • }
  • );
  •  
  • });
  •  
  • </script>

Notice that the event handler for the custom event, "inputBlur", returns the value, false. This will signal to the jQuery event management mechanism to stop the event from bubbling up through the document. And, since both our P and our DIV tags are listening for and then terminating this event, only the anscestor closest to the triggering element (our Input box) will ever see and be able to respond to this event.

That last section here was kind of a tangent to where I was going with the original post; but, I thought it would be nice to see how to accomplish the same outcome using a completely different approach.




Reader Comments

Oct 6, 2009 at 10:14 AM // reply »
4 Comments

ah ha! good news. in prototype you have the infinitely useful .up() and .down() functions for doing this. it's great that jquery have followed suit.


Oct 6, 2009 at 10:17 AM // reply »
10,640 Comments

@Aidan,

That's pretty cool. jQuery also has parents(), parent(), and find(); I assume these are also like up() and down(), but they have variations in the number of elements they will return in the final collection. Since I am not sure how up() and down() work, I can't say which of them are true parallels.


Oct 6, 2009 at 11:47 AM // reply »
4 Comments

up and down are specifically for fetching a single element from the tree. otherwise you have to use ancestors / select to match on multiple elements.

basically both frameworks now have the same functions with completely different naming conventions.


Oct 6, 2009 at 11:58 AM // reply »
10,640 Comments

@Aidan,

Ahh, gotcha. Well, at least they both have them - naming aside :)


Oct 6, 2009 at 2:56 PM // reply »
8 Comments

Nice article, Ben!

@Aidan, I think .up() is different from .closest(). It's more like .parents(':first') or .parents(':eq(someindex)'). The .closest() method starts with the matched element itself and then works its way up the DOM tree until it finds a match -- unlike .up() and parents(), which start at the parent element. For example, $('#foo').closest('.myclass') could match #foo itself if it also has a class of "myclass." This makes .closest() useful for event delegation.


Oct 6, 2009 at 3:00 PM // reply »
10,640 Comments

@Karl,

Thaks Karl!


Oct 6, 2009 at 3:21 PM // reply »
4 Comments

@Karl - that's an important distinction and as you say, actually very useful. that's something I has wished up and down did in the past.


Oct 6, 2009 at 4:38 PM // reply »
4 Comments

Great post. Especially the extra effort to duplicate and give an excellent example of event bubbling.

I'm learning a bunch form you, Any Matthews, and and Jason Dean.

When are you going to continue your Connect video posts?


Oct 6, 2009 at 4:40 PM // reply »
10,640 Comments

@Mark,

Thank a lot! I know the four of us (Andy, Todd, Jason) have been super busy, but we're definitely down for some more video recordings.

Tonight, I'm gonna try to record a new jQuery Presentation that I'm working on... fingers crossed.


Oct 7, 2009 at 1:09 AM // reply »
1 Comments

Hey man,

Just wanted to throw some props your way... I saw your slide demo last night...Very nice.

-Cypherpunk

Keep it nerdy \m/


Oct 31, 2009 at 5:55 PM // reply »
10,640 Comments

@JustLaunched,

Are you referring to something I did??


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Feb 10, 2012 at 7:21 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
Update! Instead of $(eval(options.insertAfter)).after(data['insertData']); I now use: var ajaxNode = document.createElement('span'); var parent = $(eval(options.insertAfter))[0].parentNode; ... read »
Feb 10, 2012 at 6:18 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
encountered this same, what I consider, jQuery bug last week. I'm building a site in which I load some content via AJAX. This content contains Linkedin share button placeholders which Linkedin API ne ... read »
Feb 10, 2012 at 11:30 AM
Cross-Origin Resource Sharing (CORS) AJAX Requests Between jQuery And Node.js
After you understand the concepts here, this is an awesome cheatsheet for enabling CORS in just about anything http://enable-cors.org/ ... read »
JM
Feb 10, 2012 at 9:10 AM
My Safari Browser SQLite Database Hello World Example
@Amy, Here is a very good tutorial on how to use JOIN: http://www.sqltutorial.org/sqljoin-innerjoin.aspx ... read »
Feb 10, 2012 at 4:42 AM
Building A Twitter-Inspired RESTful API Architecture In ColdFusion
This is great, very useful Ben. I spotted a small typo in the api.cgm listing: <cfthrow type="Unauthroized" /> Cheers Stefan ... read »
Feb 9, 2012 at 10:35 PM
CFDirectory Filtering Uses Pipe Character For Multiple Filters (Thanks Steve Withington)
I was wondering if there would be a filter you could apply so that you got everything but what you included in the filter. As in show me all docs that are not a .pdf. ... read »
Feb 9, 2012 at 10:29 PM
Learning ColdFusion 9: Application-Specific Data Sources
@Ben, No offence, but if people were really wanting advanced features they would be using a platform like ASP.NET MVC. CFML is so structurally compromised as a tag-based scripting language that ... read »
Feb 9, 2012 at 10:03 PM
Subversion - Cleanup Failed To Process The Following Paths
@Leviaguirre, do you still have problems with this? ... read »