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.
13 Answers 13
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')
5 Comments
'selector' twice makes the encapsulation extra desirable. YMMV'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.object.parent().find('selector') includes siblings of object and their descendants.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.
7 Comments
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.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.
3 Comments
filter() there, which makes more sense.$('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')
4 Comments
$('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)) :)$('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.$('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 :)this is whatever jQuery object a plugin was called on. It could be just as well be the result of a call chain.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);
Comments
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.
Comments
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');
1 Comment
this is more than one element this.is() is already satisfied if only one of them matches.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)
Comments
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')
Comments
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.
1 Comment
If you are strictly looking in the current node(s) the you just simply do
$(html).filter('selector')
Comments
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");
Comments
Here's the right (but sad) truth:
$(selector).parent().find(oSelector).filter($(selector).find('*'))
2 Comments
$(selector) itself from the set in all cases.