jQuery's Closest() Method Returns Only One Ancestor

Posted October 6, 2009 at 9:31 AM

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":

 Launch code in new window » Download code as text file »

  • <!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:

 Launch code in new window » Download code as text file »

  • $( 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.

 Launch code in new window » Download code as text file »

  • <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.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Print Page




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 »
6,516 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 »
6,516 Comments

@Aidan,

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


Oct 6, 2009 at 2:56 PM // reply »
5 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 »
6,516 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 »
3 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 »
6,516 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 »
6,516 Comments

@JustLaunched,

Are you referring to something I did??


Post Comment  |  Ask Ben

Recent Blog Comments
Nov 21, 2009 at 11:03 AM
Groovy Operator Overloading Does Not Work In The ColdFusion Context
Hi Ben, Thanks for this informative post. Now I am reading ur old posts too ... read »
Nov 21, 2009 at 10:56 AM
HostMySite.com Has The Best ColdFusion Hosting
@Mehul, Yes very nice people, however several downtimes per day which was not acceptable. Hence we had to move out. I am glad you are having good luck with them so far. ... read »
Nov 20, 2009 at 11:32 PM
Five Months Without Hungarian Notation And I'm Loving It
I've used headless camel case for years for not only ColdFusion variables, but also SQL tables and fields... pretty much everything involving code. I also subscribe to the "don't abbreviate and clea ... read »
Nov 20, 2009 at 11:00 PM
Five Months Without Hungarian Notation And I'm Loving It
@Marcel, Yeah, I always err on the side of longer but more readable variable names. As for the camel casing of CF methods and the headless camel casing of custom items, I get around this by always ... read »
Nov 20, 2009 at 10:56 PM
Five Months Without Hungarian Notation And I'm Loving It
I use the following and love it: my.namespace.MyComponents.functionMethodsOrUDF() CONSTANT_VALUES_OR_PROPERTIES One thing I always try is to CamelCaseBuiltInColdFusionFunctions() so others can tell ... read »
Nov 20, 2009 at 5:38 PM
Learning ColdFusion 8: CFImage Part I - Reading And Writing Images
Hi Ben, Great article. I've been looking around to see if ColdFusion image engine can programatically create the following "wrap around" effect: http://www.creativepro.com/article/photoshop-s-she ... read »
Nov 20, 2009 at 5:35 PM
Maintaining ColdFusion Sessions Across SMS Text Message Requests Without Cookies
@Dave: I talked to Gert he suggested: <cfhttp method="get" url="http://{some cf website}" result="stuff" addtoken="yes" /> Note the addition of cfhttp attribute addtoken. That should persist y ... read »