This short code snippet makes it possible to carry out multiple operations on the children of any particular parent (within the DOM) while retaining the original subject of the chain:
The Code:
// $.fn.with: // Used to process child nodes while // retaining parent within chain. $.fn.with = function(elems,action){ return this.each(function(){ $(elems,this).each(action); }); } |
Note: As dave pointed out in the comments, with
is a reserved word in JavaScript so technically shouldn’t be used as an identifier. If you’re going to use this code then it’s probably best if you change the name of it from ‘with’ to ‘withChildren’ or anything else.
Explanation:
It’s hard to explain exactly what you’d use this for, so I’ll show you instead:
Using jQuery’s native methods, you have to continually traverse the DOM to find elements, and doing this continually changes the subject of the chain. For example:
$('#elem') .hide() .find('a').each(function(){ $(this).width( $(this).width+200 ); }) .end() // Traverse back to #elem .find('em').each(function(){ $(this).width( $(this).width+100 ); }) .end() // Traverse back to #elem .fadeIn() |
As you can see, in order to re-select the parent ($('#elem')
) we have to keep on using the end()
method. This may seem intuitive to you but it can result in unnecessarily bloated code! Additionally, instead of only traversing the DOM you’re forced to traverse the chain as well; this makes keeping track of things all the more tricky!
Usage:
So, using this new ‘with’ method you might achieve the above functionality like this:
$('#elem') .hide() .with('a',function(){ $(this).width( $(this).width+200 ); }) .with('em',function(){ $(this).width( $(this).width+100 ); }) .fadeIn() |
Easy!
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
Wow, very nice and succinct. Good work.
Very impressive, thanks James 😉
Very nice! I really enjoy these tips. I’m still learning jQuery so your explanations and examples have been really helpful. Thanks!
Great! I’m glad you all like it! 🙂
I like the idea, but “with” is a Javascript reserved word…
Early versions of jQuery allowed find and filter to take a second optional argument that was a function. When that was provided it would do an each with that function using the find/filter elements and not change the chain. So your example above would just use find instead of with and it would work just the way you want.
I don’t recall why that feature was removed; I know it was the subject of some extended discussion on a jQuery list at the time.
Dave, that was my original concern; about “with” being a reserved word, but I didn’t think it mattered when used as a property/method name – it seems to work, although admittedly I haven’t yet tested it in older browsers. If not, then it can just be renamed, I only called it “with” because that seemed to make the most sense. I wasn’t aware that jQuery originally had this functionality under the ‘find’ method; I can’t imagine why they’ve removed it…
It’s easy enough to bring back though:
The problem now is that it doesn’t quite make sense… should it return the original element or the new-found elements? The point in the “with” method was that it didn’t change the subject of the chain…
The old jQuery methods returned the original elements if you provided a function, since the function had already processed the found/filtered ones. It looks like they were removed in 1.1, January 2007. I think that was the version where the stack and .end() was introduced so maybe that’s why it was considered expendable.
If you wanted to get that functionality for all methods you could do something like this:
The first arg is the name of the jQuery method you want to use, followed by its arguments (if any) and finally the function to be applied to the filtered elements. A simple implementation:
Hilarity ensues if the method isn’t one that requires an .end() or if there aren’t enough arguments.
Thanks Dave, that code will definitely come in handy! It still doesn’t make sense why they got rid of it though, it wouldn’t have done any harm to leave it there. Continually using ‘end()’ just seems so pointless…
@Dave, James: To clarify: .end() has been in jQuery since the very beginning, as has some form of a stack (although it was definitely clarified in jQuery 1.0, back in 2006).
I, personally, added in the .find(“foo”, function(){}) syntax because I liked – for the same reasons that you did – but after a while it became apparent that 1) I was the only one using it and 2) That it made a large number of methods very sloppy and ambiguous.
We can certainly look at adding it back in some day – but I’m just not seeing it, right now.
comments are ok now. Great blog btw 🙂
Hi John!
Thanks for clearing that up! I do understand why you removed it, although it does seem odd that it wasn’t used a lot, – probably not because it wasn’t useful but because people simply didn’t know it was there. As you know, the same functionality can be achieved in numerous other ways so I guess people didn’t even bother enquiring.
BTW, awesome work on Sizzle, it rules! 🙂