jQuery Plugin: From - Filtering A Collection Based On Ancestors

Posted October 12, 2009 at 9:07 PM by Ben Nadel

Tags: Javascript / DHTML

I was playing around with a customized version of jQuery's live() method when it occurred to me that being able to filter a given jQuery collection based on a set of potential ancestors might make a useful plugin. And so, I came up with the from() plugin. From() takes a collection target ancestors and filters the existing stack down to only those elements that are descendants of the given ancestor pool:

  • $( "..." ).from( "#some-element" )

Before we look at how this plugin works, let's take a look at an example to see how it might be used:

  • <!DOCTYPE HTML>
  • <html>
  • <head>
  • <title>Filtering A jQuery Collection Based On Ancestors</title>
  • <script type="text/javascript" src="jquery-1.3.2.js"></script>
  • <script type="text/javascript" src="jquery.from.js"></script>
  • <script type="text/javascript">
  •  
  • // When the DOM is ready, run scripts.
  • $(function(){
  •  
  • $( "li" )
  • .css( "font-style", "italic" )
  • .from( "#list-two" )
  • .css( "background-color", "gold" )
  • .end()
  • .css( "font-weight", "bold" )
  • ;
  •  
  • });
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <h1>
  • Filtering A jQuery Collection Based On Ancestors
  • </h1>
  •  
  • <ul id="list-one">
  • <li>
  • This is a list element.
  • </li>
  • <li>
  • This is a list element.
  • </li>
  • </ul>
  •  
  • <ul id="list-two">
  • <li>
  • This is a list element.
  • </li>
  • <li>
  • This is a list element.
  • </li>
  • </ul>
  •  
  • </body>
  • </html>

As you can see, once the DOM is ready to be interacted with, we use jQuery to grab all the list item elements and immediately make them italic. Then, will filter that collection down to only those list item elements that are descendants of the list with id, "list-two." Those items are given a gold background. Then, using the end() method, we travel back up the collection stack, and make all list item elements bold. Running the above code, we get the following output:

 
 
 
 
 
 
From() - A jQuery Plugin To Filter A Collection Based On Potential Ancestors. 
 
 
 

As you can see, the from() execution filtered the list items collection down to only those that were in the second list. As such, only the latter two items have a gold background.

Now, Let's take a look at the code that powers this plugin:

jquery.from.js

  • // Create a self-executing function closure so that we map the
  • // jQuery library to the $ variable without any global conflicts.
  • (function( $ ){
  •  
  • // This plugin will filter the current collection down to the
  • // items that are descendants of the given collection of
  • // potential ancestors.
  • $.fn.from = function( ancestors ){
  • // Make sure the ancestors object is a jQuery collection.
  • // This will work for a selector or an existing jQuery
  • // collection.
  • ancestors = jQuery( ancestors );
  •  
  • // We want to filter the list of items down to the
  • // descendants of the given ancestor collection. For this,
  • // we will need to examine each item individually.
  • return (
  • this.filter(
  • function(){
  • // Get the complete list of ancestors of the
  • // current item.
  • var myAncestors = $( this ).parents();
  •  
  • // Travel up the ancestor chain to see if any
  • // of the nodes is in the collection of target
  • // ancestors.
  • for (var i = 0 ; i < myAncestors.size() ; i++){
  •  
  • // If the current ancestor is in the target
  • // collection, return TRUE and the current
  • // node will be included by Filter().
  • if (ancestors.index( myAncestors[ i ] ) >= 0){
  • return( true );
  • }
  •  
  • }
  •  
  • // If we made it this far, then none of the
  • // current node's ancestors was in the
  • // collection of parent nodes.
  • return( false );
  • }
  • )
  • );
  • }
  •  
  • })( jQuery );

The magic behind this plugin is the fact that jQuery's filter() method can take a function for use in the filtering logic. Not only does this allow us to filter one element at a time, but, by using the filter() method, jQuery maintains the collection stack internally, allowing us to use the end() method to move back up the stack when necessary.

Anyway, just a small jQuery plugin, but I think it will make some of the other plugins that I am tinkering with easier to write.




Reader Comments

Oct 13, 2009 at 1:53 PM // reply »
19 Comments

Ben, looks useful. One suggestion though, I'd love to see your code include a private from method that both $.fn.from and $.expr[':'].from utilize. That way you could use from as both a method and a psuedo-selector!


Oct 13, 2009 at 2:31 PM // reply »
11,246 Comments

@Ben,

Ahh, very cool idea! You are the plugin master :) Let me see what I can do.


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

Hate to be the noob here, but why don't you use .foreach() instead of the for loop in your code? Is it to do with performance or something obvious I'm not seeing?


Oct 13, 2009 at 4:05 PM // reply »
11,246 Comments

@Ettiene,

Yeah, exactly. Since it's being used inside of a plugin, I figured I would opt for the slightly more performant native loop rather than use the each() iterator.


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

Thanks for the quick answer, and yeah I also meant the .each() iterator :)

Your jQuery tutorial got me hooked on jQuery, even though I'm still learning. Keep up the good work!


Oct 13, 2009 at 4:11 PM // reply »
11,246 Comments

@Ettiene,

No problem my man. Glad you like. Normally, I would just have used each(); but, my concern was that the filter() method might have to turn around and run this against a large number of comparisons - NxM - and I just panicked and wanted it to be as fast as possible.


Nov 13, 2009 at 1:55 PM // reply »
10 Comments

woot! this plugin of yours (and you) got a mention in the first official jquery podcast!

http://blog.jquery.com/2009/11/13/announcing-the-official-jquery-podcast/

(in 'this week in jquery' section on the above page and around 1:11 in the podcast)

congrats, Ben!


Nov 15, 2009 at 7:16 PM // reply »
11,246 Comments

@Azadi,

Oh bad ass! I'll have to listen to that ASAP! Thanks for letting me know.


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
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 25, 2013 at 10:01 PM
My Experience With AngularJS - The Super-heroic JavaScript MVW Framework
@Avi, Really glad to help! @Jaredwilli, I'm finding a this image hits home with a lot of people :) Hopefully we can all work through the rough patches together! @Prateek, AngularJS has error ... read »
May 25, 2013 at 9:53 PM
Nested Views, Routing, And Deep Linking With AngularJS
@Mrsean2k, I'm glad I could help! I haven't been able to keep up with the ui-router stuff. I keep saying that I'll carve out time, but I just haven't gotten to it :( ... read »
May 25, 2013 at 9:49 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, Thanks for the book recommendations. I am looking them up right now. I can see that Object Thinking is available for the Kindle App - sweet! Also, I just recently heard Martin Fowler on the ... read »
May 25, 2013 at 9:41 PM
HashKeyCopier - An AngularJS Utility Class For Merging Cached And Live Data
@Chris, I'm super excited to hear that my posts are helpful. I am also loving AngularJS; but, it definitely has some caveats and some odd behaviors and some things that just don't seem to "wor ... read »
May 25, 2013 at 9:36 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Adam, @Jason, After reading these comments, I double-checked my latest implementation and I am happy to report that I am using listFirst() and listRest(). ... read »
May 25, 2013 at 9:31 PM
Using "//" And ".//" Expressions In XPath XML Search Directives In ColdFusion
@Daxesh, I am not sure I understand the question about the current node. If you already have a reference to the current node, why would you need to query for it? As for parent node, I believe that ... read »
May 25, 2013 at 10:08 AM
Using "//" And ".//" Expressions In XPath XML Search Directives In ColdFusion
@Ben, my question is that i want the current node with its tag and its parent node. i just want only that data. So, give me the solution for that. and remember solution is working on " xpath 1.0 ... read »
May 25, 2013 at 10:01 AM
Using "//" And ".//" Expressions In XPath XML Search Directives In ColdFusion
hey ben, i want get my current node tag and also want the root node tag withing. So, how can i fix it.. ! ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools