4

I have searched a lot for adding active class to the parent menu using javascript.

I found many more examples but not a single one is working for me, below is my code

HTML

<div id="menu1" class="hmenu">
    <ul>
        <li><a href="#">Item1</a>
            <ul>
                <li><a href="#">SubItem1</a>
                    <ul>
                        <li><a href="#">SubSubItem1</a></li>
                        <li><a href="#">SubSubItem2</a></li>
                    </ul>
                </li>
                <li><a href="#">SubItem2</a> </li>
                <li><a href="#">SubItem3</a>
                    <ul>
                        <li><a href="#">SubSubItem1</a></li>
                        <li><a href="#">SubSubItem2</a></li>
                    </ul>
                </li>
            </ul>
        </li>
        <li><a href="#">Item2</a></li>
        <li><a href="#">Item3</a>
            <ul>
                <li><a href="#">SubItem1</a>
                    <ul>
                        <li><a href="#">SubSubItem1</a></li>
                        <li><a href="#">SubSubItem2</a></li>
                    </ul>
                </li>
            </ul>
        </li>
    </ul>
    <br style="clear: left" />
</div>

My requirement is when i click on SubItem1 then both Item1 and SubItem1 should be active.

And when i click on SubSubItem1 then SubSubItem1 ,SubItem1 and Item1 should be active.

Means when click on any link then its all parent link and the same link should be active.

I have tried with this javascript code :

$(document).ready(function () {
        $('.hmenu ul li ul').find('li').click(function () {
            //removing the previous selected menu state
            $('.hmenu').find('li.active').removeClass('active');
            //adding the state for this parent menu
            $(this).parents('li').addClass('active');
        });
    });

Actually i don't have any experience with javascript coding and unable to figure out the problem in my code.

Can anyone suggest me for the same.

2
  • 1
    With your current selector (.hmenu ul li ul').find('li') you aren't getting al your lis. Commented Jun 27, 2015 at 9:37
  • actually i don't know that what i am getting with this selector, but when i click on an item there is no change in menu Commented Jun 27, 2015 at 9:41

3 Answers 3

6

The issue comes from .find('li').click().

As you use nestsed <li>, this will cause the event to be fired two times when you click on a child <li>. This causes problems. Can not you add the click() to <a> elements?

$(document).ready(function () {
    $('.hmenu a').click(function () {
        //removing the previous selected menu state
        $('.hmenu').find('li.active').removeClass('active');
        //adding the state for this parent menu
        $(this).parents("li").addClass('active');

    });
});

It works just fine: https://jsfiddle.net/6put8tdx/


Note that your page will be bumped to the top while clicking to a tab because of # anchor. If you want to prevent this, you may pass the event to the function .click(function (event) {...} and add event.preventDefault inside.

Sign up to request clarification or add additional context in comments.

11 Comments

thank u so much it works fine in the fiddle ,but in my page it is not working.
my page is in .aspx and i am putting the js code in my master page header
@TufanChand Maybe should you open a new question because I really can not help you to this point, sorry.
$('a').click ? [slowmotion]NOOOO[/slowmotion]
@RokoC.Buljan Is this considered as bad practice?
|
3

If you need the click target to be the LI element (as opposed to Delgan's answer)

you can use .not() over the targeted LI's parents to prevent messing with the bubbling event targets:

$(document).ready(function () {

  $('.hmenu').find('li').click(function(event) {
    event.preventDefault();                   // Prevent page jumps due to anchors
    var $par = $(event.target).parents("li"); // get list of parents
    $(".hmenu .active").not( $par ).removeClass("active");  // not them
    $(this).addClass('active');               // let the event propagation do the work
  });

});

$(document).ready(function () {

  $('.hmenu').find('li').click(function(event) {
    event.preventDefault(); 
    var $par = $(event.target).parents("li");
    $(".hmenu .active").not($par).removeClass("active");
    $(this).addClass('active');
  });

});
.active > a{
  background: gold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

    <div id="menu1" class="hmenu">
      <ul>
        <li><a href="#">Item1</a>
          <ul>
            <li><a href="#">SubItem1</a>
              <ul>
                <li><a href="#">SubSubItem1</a></li>
                <li><a href="#">SubSubItem2</a></li>
              </ul>
            </li>
            <li><a href="#">SubItem2</a> </li>
            <li><a href="#">SubItem3</a>
              <ul>
                <li><a href="#">SubSubItem1</a></li>
                <li><a href="#">SubSubItem2</a></li>
              </ul>
            </li>
          </ul>
        </li>
        <li><a href="#">Item2</a></li>
        <li><a href="#">Item3</a>
          <ul>
            <li><a href="#">SubItem1</a>
              <ul>
                <li><a href="#">SubSubItem1</a></li>
                <li><a href="#">SubSubItem2</a></li>
              </ul>
            </li>
          </ul>
        </li>
      </ul>
      <br style="clear: left" />
    </div>

To better understand the above
The following example works out-of-the-box, and the clicked one and all it's LI parents get the "active" class.
Why? Cause the event target is li, means any li of .hmenu - so that click is attached to any of them, and clicking the subsub LI the event will propagate to the LI parents - triggering the same click behavior (this add class)!

$(".hmenu").on("click", "li", function(){
   $(this).addClass("active"); // Wow! Event propagation rulez!!
});

But we need to remove existing .active and here it gets messy...

$(".hmenu").on("click", "li", function(){
   $(".active").removeClass("active"); // triggered on every event bubble :(
   $(this).addClass("active"); // leaving only the main parent with active class
});

That's caused by the concurrency that happens while the event bubbles and triggers the same actions for the parent elements.

One way to prevent that concurrency would be using a setTimeout of 1ms:

$(".hmenu").on("click", "li", function(){
   $(".active").removeClass("active");
   setTimeout(function(){  // Let the previous finish the bubbling mess
      $(this).addClass("active"); // Yey! all fine! Every LI has the active class
   }, 1);
});

But here the timeout of 1ms can lead to visual "blinking" issues.

2 Comments

very good explanation thank you. As @Delgan's solution works for me so i m marking his answer as correct answer
@TufanChand sure! He's got a good answer. I just wanted to shad some light on the real issues for a better understanding! :) thx for the reply and happy coding
-2

Try this:

 $(function () {
     $("li a")
       .on("click", function () {
         $(this).toggleClass("active");
         $(this).closest("ul").parent().children("li a").toggleClass("active")
       .parent().parent().parent().children("li a").toggleClass("active");

     });
 });

fiddle

Traverse from the clicked element. And use toggleClass() to avoid the mundane checking if hasclass removeClass ...

1 Comment

Be honest you did not tested your example. Also, there's a better way to do .parent().parent().parent().... stackoverflow.com/questions/11568722/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.