153

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.

0

12 Answers 12

171

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
  • 5
    I've bundled this up as a plugin jquery.findIncludeSelf, registered with bower. See github.com/ronen/jquery.findIncludeSelf
    – ronen
    Commented Apr 30, 2014 at 10:41
  • 19
    @ronen An extra file for something which can be done in one line? Thanks, but no thanks.
    – user659025
    Commented 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
    – ronen
    Commented 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 use object.parent().find('selector')??? — that being said I like the idea of a lib that does it for you.
    – Sam
    Commented Dec 1, 2016 at 17:08
  • 12
    @circa1983 object.parent().find('selector') includes siblings of object and their descendants.
    – Robert
    Commented Dec 3, 2016 at 23:03
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.

7
  • 7
    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.
    – John K
    Commented May 13, 2010 at 15:59
  • 3
    The second one would be mindbogglingly slow. The first is simply just slow.
    – Tgr
    Commented 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
  • 3
    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.
    – John K
    Commented May 13, 2010 at 16:08
  • 17
    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
    – kkara
    Commented Jul 8, 2013 at 15:58
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.

3
  • 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
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')
4
  • 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.
    – Tgr
    Commented 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.
    – Tgr
    Commented May 13, 2010 at 20:23
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);
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.

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');
1
  • Ah, no... If this is more than one element this.is() is already satisfied if only one of them matches. Commented Jun 3, 2020 at 23:23
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)

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')
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.

1

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

$(html).filter('selector')
-2

Here's the right (but sad) truth:

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

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

2
  • doesn't work with detached nodes (and the document itself), however. Commented Dec 30, 2013 at 9:02
  • 2
    Uh, no, this will remove $(selector) itself from the set in all cases. Commented Dec 30, 2013 at 9:02

Not the answer you're looking for? Browse other questions tagged or ask your own question.