jQuery select descendants, including the parent

12,552

Solution 1

If I understand you correctly:

$(currentDiv).contents().addBack('.foo').css('color','red');

I renamed the "div" to "currentDiv" for clarity. This selects the current element and all of the elements it contains, then filters out the ones that do not have class foo and applies the style to the remainder, i.e., the ones that do have class foo.

EDIT A slight optimization

$(currentDiv).find('.foo').addBack('.foo').css('color','red');

EDIT

This answer has been updated to incorporate newer jQuery methods. It was originally

$(currentDiv).find('.foo').andSelf().filter('.foo').css('color','red');

which is still required for jQuery older than 1.8

Solution 2

jQuery 1.8 introduced .addBack(), which takes a selector, in favor of .andSelf().So tvanfosson's code becomes much more efficient as

$(currentDiv).find(".foo").addBack(".foo").css("color", "red");

If you didn't have that, I think that

$(currentDiv).find(".foo").add(currentDiv.filter(".foo")).css("color", "red");

would be pretty efficient, if not very pretty.

Solution 3

Andrew's answer was so useful and the most efficient of all the answers (from jQuery 1.8 onward), so I did as Sam suggested and turned it into a trivial jQuery extension method for everyone to use:

Extension code:

jQuery.fn.findAndSelf = function (selector){
    return this.find(selector).addBack(selector);
};

use like this:

$(function(){
    $('#obj').findAndSelf('.foo').css('color', 'red');
});

JSFiddle: http://jsfiddle.net/BfEwK/

Full credit to Andrew for suggesting addBack() as the best option (as at this date). Have upvoted him accordingly and suggest everyone do the same.

Solution 4

Barring a nicer solution, I've created a new function and use it instead of $:

var _$ = function(expr, parent){ 
   return $(parent).is(expr) ? $(expr, parent).add(parent) : $(expr, parent); 
}
Share:
12,552

Related videos on Youtube

Sam
Author by

Sam

Updated on October 28, 2020

Comments

  • Sam
    Sam over 3 years

    Consider the following HTML:

    <div class="foo" id="obj">
       I should be changed red
       <div class="bar" style="color:black;">
          I should not be changed red.
          <div class="foo">I should be changed red.</div>
       </div>
    </div>
    

    Given a DOM element obj and an expression, how do I go about selecting any children and possibly obj? I'm looking for something similar to "select descendants" but also including the parent, if it matches the expression.

    var obj = $("#obj")[0];
    
    //wrong, may include siblings of 'obj'
    $(".foo", $(obj).parent()).css("color", "red");
    
    //wrong -- excludes 'obj'
    $(".foo", obj).css("color", "red");
    
    //correct way, but it's annoying
    var matches = $(".foo", obj);
    if ($(obj).is(".foo")) matches = matches.add(obj);
    matches.css("color", "red");
    

    Is there a more elegant solution to this?

    • Gone Coding
      Gone Coding almost 10 years
      +1: Great question. Just hit this situation today. Have taken Andrew's answer and your comment and added a trivial jQuery findAndSelf extension as you suggested for everyone to use. Cheers.
  • Sam
    Sam over 15 years
    This would select all .foo in the document, and all children of .foo
  • rz.
    rz. over 15 years
    Right... I thought that's what you meant.
  • Sam
    Sam over 15 years
    Ahh, sorry, I've clarified the question.
  • rz.
    rz. over 15 years
    Ok now the answer is: all divs of class foo and all their children. Satisfactory?
  • Sam
    Sam over 15 years
    The answer is "all children of [some object] that match [expression] as well as [some object] if it matches [expression]"
  • Sam
    Sam over 15 years
    Ah, thank you... the "andSelf()" is what I was missing. Unfortunately this method is ineffecient... all of the children and found and then all are traversed to be filtered, rather than just a single pass.
  • Gone Coding
    Gone Coding almost 10 years
    No. They only want the parent element selected if it also has .foo, but yours just includes it.
  • Gone Coding
    Gone Coding almost 10 years
    This will "work", but only because they happened to all be divs in the example. The question was how to include an initial element (if it matches) along with any descendants (that also match). What if there were more .foos outside of #obj? They would also be included (which is not the desired behavior).
  • Gone Coding
    Gone Coding almost 10 years
    This is still slightly less efficient than addback (JQ 1.8 onward) as this runs the filter a second time on the descendants even though they all already match.
  • Gone Coding
    Gone Coding almost 10 years
    Have applied your findAndSelf suggestion (good name) to Andrew's answer instead and posted for all to cut & paste.
  • Gone Coding
    Gone Coding almost 10 years
    Definitely preferred from jQuery 1.8. Have added your suggestion as a jQuery findAndSelf extension method below. Turned out very clean using addBack, full credit to you. Cheers :)
  • dsdsdsdsd
    dsdsdsdsd over 9 years
    Note: This function has been deprecated and is now an alias for .addBack(), which should be used with jQuery 1.8 and later
  • The Dembinski
    The Dembinski over 7 years
    How has nobody seemingly noticed this. You deserve a hug and a high-five.
  • Gone Coding
    Gone Coding over 7 years
    @TheDembinski: Hug accepted with thanks (I don't do high 5's however) :)