154

The jQuery find(..) traversal method doesn't include the current node - it starts with the children of the current node. What is the best way to call a find operation that includes the current node in its matching algorithm? Looking through the docs nothing immediately jumps out at me.

asked May 13, 2010 at 15:29
0

13 Answers 13

172

For jQuery 1.8 and up, you can use .addBack(). It takes a selector so you don't need to filter the result:

object.find('selector').addBack('selector')

Prior to jQuery 1.8 you were stuck with .andSelf(), (now deprecated and removed) which then needed filtering:

object.find('selector').andSelf().filter('selector')
Gras Double
16.5k9 gold badges61 silver badges56 bronze badges
answered Jul 9, 2013 at 0:46
Sign up to request clarification or add additional context in comments.

5 Comments

I've bundled this up as a plugin jquery.findIncludeSelf, registered with bower. See github.com/ronen/jquery.findIncludeSelf
@ronen An extra file for something which can be done in one line? Thanks, but no thanks.
@prc322 an extra file doesn't bother me for deployments which bundle all the javascript into a single file anyway. Generally I prefer using (tested) library methods even for simple things, rather than cluttering my code with stuff that I find harder to read and that introduces more possibilities for bugs. In this case in particular, IMHO the need to specify 'selector' twice makes the encapsulation extra desirable. YMMV
Ok so I might be being thick but so you don't have to specify 'selector' twice, ( as mentioned by @ronen ), couldn't you just use object.parent().find('selector')??? — that being said I like the idea of a lib that does it for you.
@circa1983 object.parent().find('selector') includes siblings of object and their descendants.
41

You can't do this directly, the closest I can think of is using .andSelf() and calling .filter(), like this:

$(selector).find(oSelector).andSelf().filter(oSelector)
//or...
$(selector).find('*').andSelf().filter(oSelector);

Unfortunately .andSelf() doesn't take a selector, which would be handy.

answered May 13, 2010 at 15:32

7 Comments

You even added a comment to the jquery page right after answering this question api.jquery.com/andSelf/#comment-50124533 Now that's thoroughness. Nice! I did my due diligence and 'Liked' that one too.
The second one would be mindbogglingly slow. The first is simply just slow.
@Tgr - I don't disagree, though the first shouldn't be that show unless you're dealing with a very large number of elements. If you don't need to chain you can skip re-filtering those elements for sure.
Interestingly, closest(..) includes the current DOM element and up the tree whereas all down-the-tree traversal methods like find(..) etc don't match the current element. It's as if the jQuery team purposefully implemented these for no overlap when both operations used together for a full vertical search.
Keep in mind that .andSelf() has been deprecated as of v1.8 and replaced with .addBack() that takes a selector as an argument. See api.jquery.com/addBack
|
10

Define

$.fn.findSelf = function(selector) {
 var result = this.find(selector);
 this.each(function() {
 if ($(this).is(selector)) {
 result.add($(this));
 }
 });
 return result;
};

then use

$.findSelf(selector);

instead of

$find(selector);

Sadly jQuery does not have this built-in. Really strange for so many years of development. My AJAX handlers weren't applied to some top elements due to how .find() works.

answered Jul 23, 2015 at 11:27

3 Comments

This is buggy. It adds all of "self", even if only one of them matches... -- One reason for this bug is a crappily named jQuery method...
I tried to fix the bug you have reported. Does it work properly now?
Most other answers use filter() there, which makes more sense.
5
$('selector').find('otherSelector').add($('selector').filter('otherSelector'))

You can store $('selector') in a variable for speedup. You can even write a custom function for this if you need it a lot:

$.fn.andFind = function(expr) {
 return this.find(expr).add(this.filter(expr));
};
$('selector').andFind('otherSelector')
answered May 13, 2010 at 16:27

4 Comments

This only works if you're starting with a selector though, which may not be the case. Also, it's incorrect, it would be $('selector').find('otherSelector').add($('otherSelector')), what you have now is equivalent to .andSelf(). Lastly, the .andFind() doesn't filter based on the expression, you would need to .add($(this).filter(expr)) :)
@Nick Craver: yeah, I forgot the filtering part, fixed now. It doesn't really matter if $('selector') is replaced by some other method of getting a jQuery object (if that is what you meant by not starting with a selector), add() can handle anything just as $() can.
My point was that your $('selector') may be $('selector').children('filter').closest('.class').last()...it may be in a chain and you have no idea what that object you're adding is, so the generic solution should take the previous object like the filter does :)
I still don't see why that would be a problem. this is whatever jQuery object a plugin was called on. It could be just as well be the result of a call chain.
5

The accepted answer is very inefficient and filters the set of elements that are already matched.

//find descendants that match the selector
var $selection = $context.find(selector);
//filter the parent/context based on the selector and add it
$selection = $selection.add($context.filter(selector);
answered Mar 12, 2014 at 19:27

Comments

3

I know this is an old question, but there's a more correct way. If order is important, for example when you're matching a selector like :first, I wrote up a little function that will return the exact same result as if find() actually included the current set of elements:

$.fn.findAll = function(selector) {
 var $result = $();
 for(var i = 0; i < this.length; i++) {
 $result = $result.add(this.eq(i).filter(selector));
 $result = $result.add(this.eq(i).find(selector));
 }
 return $result.filter(selector);
};

It's not going to be efficient by any means, but it's the best I've come up with to maintain proper order.

answered Aug 29, 2013 at 21:04

Comments

3

If you want the chaining to work properly use the snippet below.

$.fn.findBack = function(expr) {
 var r = this.find(expr);
 if (this.is(expr)) r = r.add(this);
 return this.pushStack(r);
};

After the call of the end function it returns the #foo element.

$('#foo')
 .findBack('.red')
 .css('color', 'red')
 .end()
 .removeAttr('id');

Without defining extra plugins, you are stuck with this.

$('#foo')
 .find('.red')
 .addBack('.red')
 .css('color', 'red')
 .end()
 .end()
 .removeAttr('id');
answered Jun 7, 2016 at 15:37

1 Comment

Ah, no... If this is more than one element this.is() is already satisfied if only one of them matches.
3

In case you are looking for exactly one element, either current element or one inside it, you can use:

result = elem.is(selector) ? elem : elem.find(selector);

In case you are looking for multiple elements you can use:

result = elem.filter(selector).add(elem.find(selector));

The use of andSelf/andBack is pretty rare, not sure why. Perhaps because of the performance issues some guys mentioned before me.

(I now noticed that Tgr already gave that second solution)

answered Jan 1, 2018 at 14:17

Comments

2

I was trying to find a solution which does not repeat itself (i.e. not entering the same selector twice).

And this tiny jQuery extention does it:

jQuery.fn.findWithSelf = function(...args) {
 return this.pushStack(this.find(...args).add(this.filter(...args)));
};

It combines find() (only descendants) with filter() (only current set) and supports whatever arguments both eat. The pushStack() allows for .end() to work as expected.

Use like this:

$(element).findWithSelf('.target')
answered Jun 4, 2020 at 8:55

Comments

1

I think andSelf is what you want:

obj.find(selector).andSelf()

Note that this will always add back the current node, whether or not it matches the selector.

answered May 13, 2010 at 15:31

1 Comment

As the linked page mentions, it was removed on 3.0.
1

If you are strictly looking in the current node(s) the you just simply do

$(html).filter('selector')
answered Oct 20, 2017 at 10:07

Comments

0

Just to summarize the answer of Gras Double and Robert and to avoid the insertion of duplicated select statements, it is possible to add a single extension as jQuery function:

$.fn.findWithSelf = function (s) {
 return this.find(s).addBack(s);
};

You can add this to the start of your script. It extends your jQuery plugin so that, subsequently, you can just call:

$("selector1").findWithSelf("selector2");
answered Feb 13, 2025 at 12:40

Comments

-2

Here's the right (but sad) truth:

$(selector).parent().find(oSelector).filter($(selector).find('*'))

http://jsfiddle.net/SergeJcqmn/MeQb8/2/

answered Nov 26, 2013 at 13:41

2 Comments

doesn't work with detached nodes (and the document itself), however.
Uh, no, this will remove $(selector) itself from the set in all cases.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.