0

I need to check if a <ul> exists inside an <li> that has the class .sub-menu and if it does, add a class to the anchor tag inside the <a> tag above the .sub-menu

Using jQuery to see if a div has a child with a certain class

How to detect if any child elements within a parent element has a certain class?

Above are my reference answers.

I'm trying to loop through each list and check with .find() I don't understand how to use .find() in a loop or each() function and how to tie it to the this keyword.

I also tried .children() but it throws that children() is not a function

See the first example below.

$(function(e) {
var getMenuItems = $(".item");
  for (var i = 0; i < getMenuItems.length; i++) {
    if (this.find("ul.sub-menu") !== 0) {
      console.log("sub-menu found")
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li class="item">
    <a href="#"></a>
    <ul class="sub-menu">
        <li></li>
    </ul>
   </li>
  <li class="item">
    <a href="#"></a>
  </li>
  <li class="item">
    <a href="#"></a>
    <ul class="sub-menu">
        <li></li>
    </ul>
  </li>
  <li class="item">
    <a href="#"></a>
  </li>
</ul>

it throws this.find is not a function - why? Can you not use this in .find()? Is this not referring to what I think it is?

I don't understand.

I tried this too:

$(function(e) {
var getMenuItems = $(".item");
  for (var i = 0; i < getMenuItems.length; i++) {
    if (getMenuItems.find("ul.sub-menu") !== 0) {
      console.log("sub-menu found")
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li class="item">
    <a href="#"></a>
    <ul class="sub-menu">
        <li></li>
    </ul>
   </li>
  <li class="item">
    <a href="#"></a>
  </li>
  <li class="item">
    <a href="#"></a>
    <ul class="sub-menu">
        <li></li>
    </ul>
  </li>
  <li class="item">
    <a href="#"></a>
  </li>
</ul>

The error goes away, but it logs 4 times when I expect it to be 2. This is because it's just looping 4 times I think? Howcome .find() doesn't work in this example? What is this exactly referring to in these examples? My understanding was that if you use this in a loop it's referring to each element it's looping through. But I guess that's not the case.

What needs to change with this code snippet so that I can hit the 2 sections with .sub-menu and modify the DOM accordingly?

kawnah
  • 3,204
  • 8
  • 53
  • 103
  • 2
    `this` isn't what you think it is there. – Kevin B Nov 28 '18 at 17:10
  • It logs 4 times because there's 4 things you are looping over. – Kevin B Nov 28 '18 at 17:11
  • You also don't actually need to do any looping here. Just select your target elements and do work, looping will occur implicitly. – Kevin B Nov 28 '18 at 17:12
  • 1
    Did you mean `$(this)` in the first attempt? If so, you probably meant `$(this).find("ul.sub-menu").length !== 0` – trincot Nov 28 '18 at 17:12
  • Want to know what `this` is.... log it to console ...`console.log(this)` – charlietfl Nov 28 '18 at 17:15
  • @charlietfl I think I missed it in my question but it logs the entire DOM and not the list element. Why does it do that? – kawnah Nov 28 '18 at 18:21
  • Because nothing in the loop changes it from what it is outside the loop and it has nothing to do with the list items to begin with. It is what `$(function(){ })` scopes it to be – charlietfl Nov 28 '18 at 18:23
  • [How does `this` keyword work](https://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work) – charlietfl Nov 28 '18 at 18:27
  • @trincot oh right I forgot - that is what I meant and why I couldn't get it. – kawnah Nov 28 '18 at 18:32

2 Answers2

1

You using jQuery so you can use each method to achieve what you looking for. See snippet in below:

$(function(e) {
  var getMenuItems = $(".item");
  getMenuItems.each(function( index ) {
   if ($(this).find("ul.sub-menu").length > 0) {
      console.log("sub-menu found")
    }
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li class="item">
    <a href="#"></a>
    <ul class="sub-menu">
        <li></li>
    </ul>
   </li>
  <li class="item">
    <a href="#"></a>
  </li>
  <li class="item">
    <a href="#"></a>
    <ul class="sub-menu">
        <li></li>
    </ul>
  </li>
  <li class="item">
    <a href="#"></a>
  </li>
</ul>
Hanif
  • 3,739
  • 1
  • 12
  • 18
0

A few issues:

  • In the callback passed to $(), this is the document DOM object. On the other hand .find is a jQuery method -- not a DOM method -- so that explains the error in the first attempt. Note that in general you should use $(this) in jQuery callbacks (there are exceptions), which then allow you to call methods like .find.

  • The comparison with zero is not the right way to test if find returns a match. find returns a jQuery object which is array-like, so you should really do this:

    if ($(this).find("ul.sub-menu").length !== 0)
    
  • Although $(this) now returns a jQuery object, it is the same object in each iteration of the for loop, and so you get 4 outputs, and in each case the tested .length value is in fact 2. You actually want this to refer to the elements in getMenuItems. For that use the callback passed to getMenuItems.each. In there this will refer to an "item" element. So then the above if condition will become correct:

    getMenuItems.each(function () {
        if ($(this).find("ul.sub-menu").length !== 0) {
            console.log("sub-menu found");
        }
    }); 
    

But I would suggest to not use find at all, but a smarter selector:

$(function() {
    $(".item > ul.sub-menu").each(function () {
       console.log("sub-menu found");
    });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li class="item">
    <a href="#">A</a>
    <ul class="sub-menu"><li>Sub A.1</li></ul>
   </li>
  <li class="item">
    <a href="#">B</a>
  </li>
  <li class="item">
    <a href="#">C</a>
    <ul class="sub-menu"><li>Sub C.1</li></ul>
  </li>
  <li class="item">
    <a href="#">D</a>
  </li>
</ul>
trincot
  • 317,000
  • 35
  • 244
  • 286