0

If I do:

$("a").on("click", function(e) {
    $(this).parent().find("ul").toggleClass("closed opened");
    e.preventDefault();
});

It works but it will target all uls of the parent

so I am doing

$("a").on("click", function(e) {
    $(this).parent().next("ul").toggleClass("closed opened");
    e.preventDefault();
});

But this doesn't work, I get no errors at all.

If I do the following works on JSFiddle using the html used here:

$("a").on("click", function(e) {
    $(this).next("ul").toggleClass("closed opened");
    e.preventDefault();
});

It doesn't make sense. I need to target the first <ul> child of the parent element that I am clicking.

<ul>
  <li><a href="">Text</a>
     <ul class="closed">
        <li><a href="">Two</a>
           <ul class="closed">
              <li><a href="">Three</a>
                <ul class="closed">
                   <li><a href="">Four</a></li>
                </ul>
              </li>
           </ul>
         </li>
     </ul>
   </li>
</ul>

.closed {
    display: none;
}

.opened {
    display: block;
}

JSFiddle with the html used in the example

UPDATE

Thanks to comment due to my real html having a <span> element I had to use .nextAll()

Actual html

<ul>
  <li>
     <a href="">Text</a>
     <span></span>
     <ul class="closed">
        <li>
           <a href="">Two</a>
           <span></span>
           <ul class="closed">
              <li>
                <a href="">Three</a>
                <span></span>
                <ul class="closed">
                   <li><a href="">Four</a></li>
                </ul>
              </li>
           </ul>
         </li>
     </ul>
   </li>
</ul>

This is the JSFidle with the real html

rob.m
  • 9,843
  • 19
  • 73
  • 162

4 Answers4

3

what you are looking for is siblings() instead of next()

$("a").on("click", function(e) {
    $(this).siblings("ul").toggleClass("closed opened");
    e.preventDefault();
});

The reason why next is not working with you is in the way that you tried to use it. you have used parent() first, then asked to get the next() element of type ul, and this doesn't match the way next should work.

cause in your DOM structure, the next element after going up to the parent will be your anchor tag, and next will only match with just the first element in your dom, not with the first element that matches with your selector, and this is the root cause of your mis-usage.

consider the official description of how next with a selector is working:

The method optionally accepts a selector expression of the same type that we can pass to the $() function. If the immediately following sibling matches the selector, it remains in the newly constructed jQuery object; otherwise, it is excluded.

nextall will also do the trick.

Mohammed Swillam
  • 9,119
  • 4
  • 36
  • 47
  • 1
    Why is that? Why isn't `.next` enough? – Felix Kling Nov 28 '16 at 00:22
  • That's it. ! Thanks a lot – rob.m Nov 28 '16 at 00:23
  • @FelixKling that is exactly why I've asked the question, check my JSFiddle, I don't understand why – rob.m Nov 28 '16 at 00:24
  • @rob.m: Uh? You said it works with `.next`: "*If I do the following works:"* – Felix Kling Nov 28 '16 at 00:25
  • @FelixKling it does on JSFiddle, but not on my live site – rob.m Nov 28 '16 at 00:29
  • @rob.m: kindly check my updated clarification above, hope it clarify what is going on for you :) – Mohammed Swillam Nov 28 '16 at 00:29
  • @rob.m: Then the document structure in your example is different than it is on your actual site. You should update your example. – Felix Kling Nov 28 '16 at 00:29
  • *"The reason why next is not working with you is in the way that you tried to use it. you have used parent() first,"* But the OP also did this: `$(this).next("ul").toggleClass("closed opened");`, which worked. If `.siblings` works but `.next` doesn't, then there must be another element between the `` and the `
      ` element, which is the actual problem.
    – Felix Kling Nov 28 '16 at 00:30
  • @FelixKling there you go, copy/paste from my real html https://jsfiddle.net/3d3nzj42/7/ – rob.m Nov 28 '16 at 00:31
  • 1
    after checking your actual html, my clarification is still correct. your `next` element to the anchor is a `span` element, and not a `ul`, so by using `siblings` it will look for your selector in all siblings, but `next only gets the first item and stops. – Mohammed Swillam Nov 28 '16 at 00:33
  • @rob.m: This is the solution you should use: [Efficient, concise way to find next matching sibling?](http://stackoverflow.com/a/4933275/218196) – Felix Kling Nov 28 '16 at 00:34
  • *"the next element after going up to the parent will be your anchor tag"* That's incorrect, the next sibling is the following `li` element. The problem I have with this answer is that it was pure guess (and didn't provide any explanation initially). `.next` was a correct solution for the original problem statement. – Felix Kling Nov 28 '16 at 00:36
  • @FelixKling are you saying that the problem is due to having an extra el ? Because that's the only difference, yet the is a children of .parent("ul") of the clicked el just as much as it is its next
      , no?
    – rob.m Nov 28 '16 at 00:40
  • @rob.m: Yes, that's exactly the issue. Have a look at the documentation of `.next` again. `.next` selects the *next sibling* *if and only if* it matches the provided selector. Since the next element is a `` element it doesn't match the selector `ul`, so it is not selected. OTOH, `.nextAll` will find *all* following siblings that match the selector. This has been asked on SO quite often, e.g. http://stackoverflow.com/q/2089089/218196 . – Felix Kling Nov 28 '16 at 00:42
  • 1
    @FelixKling is see, thanks a lot, so the real answer is to use .nextAll for my real scenario – rob.m Nov 28 '16 at 00:44
  • @FelixKling yes i confirm, it works. .nextAll() is what I needed. I will leave this as accepted tho as it answers my first question. Thanks tho – rob.m Nov 28 '16 at 00:45
2

The parent of the clicked a element is a li element. The next siblings of the parent li elements can only be other li elements. So the script simply fails to select the target element and it actually makes sense as .next only selects the very next [matching] siblings.

Fortunately you have a working version!

Ram
  • 143,282
  • 16
  • 168
  • 197
  • 1
    @rob.m: `.next` selects the next **sibling**, not a **child**. https://api.jquery.com/next/ . See this image for an explanation of DOM node relationships: http://felix-kling.de/images/DOM_relationship.png . – Felix Kling Nov 28 '16 at 00:16