1

Why can't I select a child of a child in jQuery? I want to add a class to a child of a child using .children() method and the > selector. See code below:

$(function(){
        // main expansion element
        $(".expander").click(function() {
            var subShown = $(this).children("ul > li").hasClass("show");
            if (!subShown) {
                $(this).children(".indented").slideDown('100').addClass("show");
                $(this).children(".caret").addClass("reversedCaret");
            } else {
                $(this).children(".indented").slideUp('100').removeClass("show");
                $(this).children(".caret").removeClass("reversedCaret");
            }
        });
    });

HTML:

<div class="expander">
                  <span class="caret downCaret right visibleCaret">+</span>
                  <ul>
                  <li class="category">Item 1<a href="http://www.google.com"></a></li>
                  <li class="indented"><a href="http://www.google.com">Item 2</a></li>
                  <li class="indented"><a href="http://www.google.com">Item 3</a></li>
                  </ul>
                </div>

When I click on expander it will not add the class to my li elements. Why?

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
kawnah
  • 3,204
  • 8
  • 53
  • 103
  • 2
    The `li` are not direct children of the expander, which is what `children()` returns. You will need to use `find()` instead of `children()`. `children()` is essentially the same as `find('> *')` – Taplar Nov 10 '17 at 21:34
  • I tried `.find()` but I couldn't get it working properly. would it go before or after `.children()`? – kawnah Nov 10 '17 at 21:37
  • 2
    Look at Scott's solution. You don't -have- to use children() at all. – Taplar Nov 10 '17 at 21:38
  • Your jQuery code is very fragile. I recommend reading [Decoupling Your HTML, CSS, and JavaScript](https://philipwalton.com/articles/decoupling-html-css-and-javascript/). – Erik Philips Nov 10 '17 at 22:07
  • @Taplar But I'm not understanding why `.children()` didn't work...so the `li` I want is two levels down from expander div, right? So `.children()` would have given me the `ul` element, and then from there if I used `>` to target the li to check for the class? What is the piece I am/was missing here? – kawnah Nov 13 '17 at 14:11
  • The selector in the `children()` is only applied against those elements, and since the li's are not direct children, the filter in children wouldn't match them. – Taplar Nov 13 '17 at 15:19

4 Answers4

2

From the JQuery documentation on .children():

The .children() method differs from .find() in that .children() only travels a single level down the DOM tree while .find() can traverse down multiple levels to select descendant elements (grandchildren, etc.) as well.

You really don't even need to be using .children() for this at all. The much simpler solution is to just provide context for your queries by passing a second argument to JQuery after the selector.

$(function(){
        // main expansion element
        $(".expander").click(function() {
        
           // Just for demonstration: *************************************************
           
            // 2 because of <span> and <ul>
           console.log($(this).children().length);          
           
           // 0 because anything beyond children isn't returned in the first place
           console.log($(this).children("ul > li").length); 
           // *************************************************************************
        
            //  By passing "this" as the second argument to JQuery, after the selector,
            //  it sets the context for the search to only "this", so the entire DOM
            //  is not searched, just the object that "this" is bound to.
            var subShown = $("ul > li", this).hasClass("show");
            if (!subShown) {
                $(".indented", this).slideDown('100').addClass("show");
                $(".caret", this).addClass("reversedCaret");
            } else {
                $(".indented", this).slideUp('100').removeClass("show");
                $(".caret", this).removeClass("reversedCaret");
            }
        });
    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="expander">
  <span class="caret downCaret right visibleCaret">+</span>
  <ul>
    <li class="category">Item 1<a href="http://www.google.com"></a></li>
    <li class="indented"><a href="http://www.google.com">Item 2</a></li>
    <li class="indented"><a href="http://www.google.com">Item 3</a></li>
  </ul>
</div>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • Thank you for this - but I'm still having a hard time understanding why my version didn't work? So if `.children()` goes down one level in dom tree, and my li is two levels down from the div. So with that in mind, shouldn't have using `.children()` and the `>` selector given me what I wanted? – kawnah Nov 13 '17 at 14:08
  • @kawnah The `.children()` method returns a wrapped set of **only** the child elements. So, even though your selector says to search deeper from there, the set of elements that the search will take place on just doesn't have any elements at a lower depth. – Scott Marcus Nov 13 '17 at 14:11
  • 1
    @kawnah I have updated the answer to include some demonstrations that show that calling `.children()` will only return the immediate child nodes and nothing else beyond that. So trying to search deeper returns nothing. – Scott Marcus Nov 13 '17 at 14:16
1

you can use .find('>ul>li'), example:

var expander = $(".expander")

expander.on('click', function(){
  var li = expender.find('>ul>li')
  li.hasClass("show")
})
Yukulélé
  • 15,644
  • 10
  • 70
  • 94
0

If you still want to approach it as you are (and not as the given answer does, which is better by the way), then change this

$(this).children("ul > li")

to this:

$(this).children('ul').children()

Selecting children of ul that is.

Tony M
  • 741
  • 4
  • 16
0

.querySelector

document.querySelector("section > div").classList.add("my-gray");
.my-gray{
  background: gray;
}
<section>
Foo
<div>
Bar
</div>
</section>
Ronnie Royston
  • 16,778
  • 6
  • 77
  • 91