Search code examples
htmljquerycsshovernested-lists

jQuery nested hover events removeClass() on mouseleave issue


I have a nested list that looks like this:

<ul class="menu-wrapper">
    <li>
        <a href="#">Menu item</a>
        <ul class="sub-menu">
            <li>
                <a href="#">Subitem-1</a>
                <ul>
                <li>Sub-Subitem-1</li>
                <li>Sub-Subitem-2</li>
                <li>Sub-Subitem-3</li>
                </ul>
            </li>
            <li>
                <a href="#">Subitem-2</a>
                <ul>
                <li>Sub-Subitem-1</li>
                <li>Sub-Subitem-2</li>
                <li>Sub-Subitem-3</li>
                </ul>
            </li>
        </ul>
    </li>
</ul>

When hovering the ul.menu-wrapper > li item, an .active class is applied to the first ul.sub-menu and the first li item within the submenu list:

$('ul.menu-wrapper').children().hover(
    function() {
        let subMenu = $(this).find('ul.sub-menu').first()
        subMenu.addClass('active')
        subMenu.children().first().addClass('active')
    },
    function() {
        let subMenu = $(this).find('ul.sub-menu').first()
        subMenu.removeClass('active')
    }
)

Now I need to hover the li items within the ul.sub-menu and change the .active class to the item being hovered.

I can do this by adding a .hover() method on the subMenu.children() items.

$('ul.menu-wrapper').children().hover(
    function() {
        ...
        subMenu.children().hover(
            function() {
                $(this).addClass('active')
            },
            function() {
                $(this).removeClass('active')
            }
        )
    },
    function() {
        ...
    }
)

When a submenu item has been hovered and the pointer goes back to the ul.menu-wrapper > li item, I want to have at least one ul.sub-menu > li item with the .active class.

Currently the mouseleave method is removing the class when pointer is moved from a submenu back to the parent list item. How can I prevent this to always have one ul.sub-menu > li with .active class at all times?

Demo https://jsfiddle.net/5Lwyh3xs/


Solution

  • On mouse out add active class to the first li if no li items have active class.

    $('ul.menu-wrapper').children().hover(
      function() {
        let subMenu = $(this).find('ul.sub-menu').first()
    
        subMenu.addClass('active')
        subMenu.children().first().addClass('active')
    
        subMenu.children().hover(
          function() {
            $(this).addClass('active').siblings().removeClass('active')
          },
          function() {
            $(this).removeClass('active')
            if($('.sub-menu > li.active').length == 0) {
                 subMenu.children().first().addClass('active')
            }
          }
        )
      },
      function() {
        let subMenu = $(this).find('ul.sub-menu').first()
    
        subMenu.removeClass('active')
      }
    )
    body {
      margin: 0;
      padding: 0;
    }
    
    ul {
      list-style-type: none;
      padding: 0;
      margin: 0;
    }
    
    ul.menu-wrapper {
      height: 80px;
    }
    
    ul.menu-wrapper {
      background: gray;
    }
    
    ul.menu-wrapper>li {
      height: 100%;
      display: flex;
      align-items: center;
    }
    
    ul.menu-wrapper ul.sub-menu {
      visibility: hidden;
      position: absolute;
      top: 80px;
    }
    
    ul.menu-wrapper ul.sub-menu.active {
      visibility: visible;
    }
    
    ul.menu-wrapper ul.sub-menu.active>li.active {
      background: darkmagenta;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <nav role="navigation" id="navigation-wrapper">
      <ul class="menu-wrapper">
        <li>
          <a href="#">Menu item</a>
          <ul class="sub-menu">
            <li>
              <a href="#">Subitem-1</a>
              <ul>
                <li>Sub-Subitem-1</li>
                <li>Sub-Subitem-2</li>
                <li>Sub-Subitem-3</li>
              </ul>
            </li>
            <li>
              <a href="#">Subitem-2</a>
              <ul>
                <li>Sub-Subitem-1</li>
                <li>Sub-Subitem-2</li>
                <li>Sub-Subitem-3</li>
              </ul>
            </li>
          </ul>
        </li>
      </ul>
    </nav>

    https://jsfiddle.net/y5qkb7mv/