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.
12 Answers
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')
-
5I've bundled this up as a plugin jquery.findIncludeSelf, registered with bower. See github.com/ronen/jquery.findIncludeSelf– ronenCommented Apr 30, 2014 at 10:41
-
19@ronen An extra file for something which can be done in one line? Thanks, but no thanks.– user659025Commented Nov 18, 2015 at 14:58
-
7@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– ronenCommented Jan 16, 2016 at 12:57 -
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 useobject.parent().find('selector')
??? — that being said I like the idea of a lib that does it for you.– SamCommented Dec 1, 2016 at 17:08 -
12@circa1983
object.parent().find('selector')
includes siblings ofobject
and their descendants.– RobertCommented Dec 3, 2016 at 23:03
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.
-
7You 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.– John KCommented May 13, 2010 at 15:59
-
3The second one would be mindbogglingly slow. The first is simply just slow.– TgrCommented May 13, 2010 at 16:05
-
@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. Commented May 13, 2010 at 16:08
-
3Interestingly,
closest(..)
includes the current DOM element and up the tree whereas all down-the-tree traversal methods likefind(..)
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.– John KCommented May 13, 2010 at 16:08 -
17Keep 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– kkaraCommented Jul 8, 2013 at 15:58
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.
-
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... Commented Mar 23, 2020 at 2:21
-
I tried to fix the bug you have reported. Does it work properly now? Commented Mar 23, 2020 at 14:50
-
Most other answers use
filter()
there, which makes more sense. Commented Mar 23, 2020 at 21:48
$('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')
-
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))
:) Commented May 13, 2010 at 16:42 -
@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.– TgrCommented May 13, 2010 at 16:55 -
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 :) Commented May 13, 2010 at 17:11 -
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.– TgrCommented May 13, 2010 at 20:23
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);
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.
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');
-
Ah, no... If
this
is more than one elementthis.is()
is already satisfied if only one of them matches. Commented Jun 3, 2020 at 23:23
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)
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')
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.
If you are strictly looking in the current node(s) the you just simply do
$(html).filter('selector')
Here's the right (but sad) truth:
$(selector).parent().find(oSelector).filter($(selector).find('*'))
-
doesn't work with detached nodes (and the document itself), however. Commented Dec 30, 2013 at 9:02
-
2Uh, no, this will remove
$(selector)
itself from the set in all cases. Commented Dec 30, 2013 at 9:02