0

I created an HTML page with a navigation menu in a sidebar. Some of the menu element contain sub-elements. What I am trying to do is:

Hide all sub-elements (this I managed) If I hover over an element that has sub-element, show the sub-elements. If I hover over an element that does not have any sub-elements, don't do anything.

I sort of know how to do this with CSS, but since this is for a jQuery class, I want to use jQuery.

I already found a few hints on the web, but I don't really know how to apply the suggestions to my code. I am completey new to jQuery, hence any help will be much appreciated!

Here's the HTML:

   <nav>
  <ul class="menu">
    <li><a href="">Home</a></li>
    <li><a href="">Page 1</a></li>
    <li><a href="">Page 2</a>
      <ul class="submenu">
        <li><a href="">Subpage 2a</a></li>
        <li><a href="">Subpage 2b</a></li>
        <li><a href="">Subpage 2c</a></li>
      </ul>
    </li>
    <li><a href="">Page 3</a></li>
  </ul>
</nav>

CSS:

nav {
    float: left;
    width: 200 px;
    position: fixed;
    top: 80px;
    left: 10px;
    border-top: 1px solid #9AA2B2;
    border-right: 1px solid #9AA2B2;
    border-left: 1px solid #9AA2B2;
}
.menu, .submenu {
  list-style: none;
  padding: 0;
  margin: 0;
}

.menu a {
    display: block;
    padding: 10px 15px;
    background: linear-gradient(to bottom, #7d7e7d 0%,#6B737E 100%); 
    border-top: 1px solid #9AA2B2;
    border-bottom: 1px solid #2E323A;
    text-decoration: none;
    color: white;
}

.menu a:hover {

    background: linear-gradient(to bottom, #3EB9E7 0%,#8abbd7 100%); 
}


.submenu a {

    padding: 10px 25px;
    background: linear-gradient(to bottom, #E2E2E2 0%,#fcfff4 100%);
    border-bottom: 1px solid #CFD0CF;
    color: black;
}

.submenu a:hover {

    background: linear-gradient(to bottom, #C5C6C5 0%,#d6dbbf 100%); 
  background-color: #C5C6C5;
}

.submenu {
  overflow: hidden;
  max-height: 0;
}

This is a jQuery snippet that I found and that seems to make sense, but I am not sure how to apply it.

$(document).ready(function () {
  $('#nav > li > a').hover(function(){
    if ($(this).attr('class') != 'deployed'){
      $('#nav li ul').slideUp();
      $(this).next().slideToggle();
      $('#nav li a').removeClass('deployed');
      $(this).addClass('deployed');
    }
  });
});

5 Answers5

1

If you want to only use jQuery to achieve this. This is how you can do it.

$(document).ready(function() {

    $('.menu > li').hover(function() {

        $(this).has('.submenu').children('.submenu').stop().slideToggle();

    });

});

So what the above code does is that on hover, it checks if the hovered element has a children with a class '.submenu' and if it does, then it selects that children and toggles it with a sliding effect. Instead of slide toggling it you could obviously do anything with it like adding a class.

The .stop() stops repetition of the sliding effect if you were to quickly toggle hover on and off several times.

Suresh
  • 427
  • 2
  • 7
0

To actually answer your question unlike these others... Basically add a mouseenter event handler, check if the li has a submenu, if so add the active class to it.

Modify it to how you want them to close

$('.menu li').mouseenter(function() {
  if($(this).has('.submenu')) {
   $(this).find('.submenu').addClass('active');
  }
 });
  
  $('.menu .submenu').mouseleave(function() {
   $('.submenu').removeClass('active');
 });
nav {
    float: left;
    width: 200 px;
    position: fixed;
    top: 80px;
    left: 10px;
    border-top: 1px solid #9AA2B2;
    border-right: 1px solid #9AA2B2;
    border-left: 1px solid #9AA2B2;
}
.menu, .submenu {
  list-style: none;
  padding: 0;
  margin: 0;
}

.menu a {
    display: block;
    padding: 10px 15px;
    background: linear-gradient(to bottom, #7d7e7d 0%,#6B737E 100%); 
    border-top: 1px solid #9AA2B2;
    border-bottom: 1px solid #2E323A;
    text-decoration: none;
    color: white;
}

.menu a:hover {

    background: linear-gradient(to bottom, #3EB9E7 0%,#8abbd7 100%); 
}


.submenu a {

    padding: 10px 25px;
    background: linear-gradient(to bottom, #E2E2E2 0%,#fcfff4 100%);
    border-bottom: 1px solid #CFD0CF;
    color: black;
}

.submenu a:hover {

    background: linear-gradient(to bottom, #C5C6C5 0%,#d6dbbf 100%); 
  background-color: #C5C6C5;
}

.submenu {
  overflow: hidden;
  max-height: 0;
 transition: 0.3s ease;
}

.submenu.active {
 max-height: 200px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav>
  <ul class="menu">
    <li><a href="#">Home</a></li>
    <li><a href="#">Page 1</a></li>
    <li><a href="#">Page 2</a>
      <ul class="submenu">
        <li><a href="#">Subpage 2a</a></li>
        <li><a href="#">Subpage 2b</a></li>
        <li><a href="#">Subpage 2c</a></li>
      </ul>
    </li>
    <li><a href="">Page 3</a></li>
  </ul>
</nav>
StefanBob
  • 4,857
  • 2
  • 32
  • 38
  • Thank you, that helped a lot. Where would I have to put the mouseleave function to close the menu again when moving the mouse away? Inside the if statement? I know it would be easer to use a toggle, but I have to do it by adding/removing a class. – Laura_Laurent Mar 23 '18 at 19:20
  • You can just put a similar mouseleave function underneath. Not sure about your requirements for closing the menu. I edited answer to show when the mouse leaves they all close – StefanBob Mar 23 '18 at 19:29
  • Thank you so much. Now it all makes sense! – Laura_Laurent Mar 23 '18 at 19:57
0

I fixed below then the sample works like below snippet (for demo purpose, remove position:fixed from ):

1. bind hover event to $('nav > ul > li > a') intead of $('#nav > li > a'), but it slideDown the sub menu when the mouse hover on the sub menu, so need to change it to $('nav > ul > li')

2. replace max-height:0 with display:none; Below is the reason using display instead of max-height.

Check JQuery API:

As JQuery Defined:

The .slideToggle() method animates the height of the matched elements. This causes lower parts of the page to slide up or down, appearing to reveal or conceal the items. If the element is initially displayed, it will be hidden; if hidden, it will be shown. The display property is saved and restored as needed. If an element has a display value of inline, then is hidden and shown, it will once again be displayed inline. When the height reaches 0 after a hiding animation, the display style property is set to none to ensure that the element no longer affects the layout of the page.

3. in the handler of hover bind, execute $('ul', $(this)).slideToggle( "slow" );

$(document).ready(function () {
  $('nav > ul > li').hover(function(){
      $('ul', $(this)).slideToggle( "slow" );
  });
});
nav {
    float: left;
    width: 200 px;
    top: 80px;
    left: 10px;
    border-top: 1px solid #9AA2B2;
    border-right: 1px solid #9AA2B2;
    border-left: 1px solid #9AA2B2;
}
.menu, .submenu {
  list-style: none;
  padding: 0;
  margin: 0;
}

.menu a {
    display: block;
    padding: 10px 15px;
    background: linear-gradient(to bottom, #7d7e7d 0%,#6B737E 100%); 
    border-top: 1px solid #9AA2B2;
    border-bottom: 1px solid #2E323A;
    text-decoration: none;
    color: white;
}

.menu a:hover {

    background: linear-gradient(to bottom, #3EB9E7 0%,#8abbd7 100%); 
}


.submenu a {

    padding: 10px 25px;
    background: linear-gradient(to bottom, #E2E2E2 0%,#fcfff4 100%);
    border-bottom: 1px solid #CFD0CF;
    color: black;
}

.submenu a:hover {

  background: linear-gradient(to bottom, #C5C6C5 0%,#d6dbbf 100%); 
  background-color: #C5C6C5;
}

.submenu {
  overflow: hidden;
  display:none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav>
  <ul class="menu">
    <li><a href="">Home</a></li>
    <li><a href="">Page 1</a></li>
    <li><a href="">Page 2</a>
      <ul class="submenu">
        <li><a href="">Subpage 2a</a></li>
        <li><a href="">Subpage 2b</a></li>
        <li><a href="">Subpage 2c</a></li>
      </ul>
    </li>
    <li><a href="">Page 3</a></li>
  </ul>
</nav>
Sphinx
  • 10,519
  • 2
  • 27
  • 45
0

You just need to make a few adjustments to your code, mostly targeting, but also a minor CSS change.

Change #nav to .menu in all jQuery code so that the correct elements are targeted.

Change max-height: 0; to display: none; in the .submenu CSS so your submenu can be shown . You can also set a min-width for your menu so the width doesn't change when the submenu is visible.

You may also choose to modify your jQuery code or add a mouseout handler so the submenu is hidden when you are no longer over the parent/submenu.

See this fiddle: https://jsfiddle.net/vbryo27z/9/

todd
  • 355
  • 3
  • 11
  • Thanks, that was very helpful! Would you mind explaining why the width of the menu changes when I open/close the menu? How can I make sure it always stays the same? – Laura_Laurent Mar 23 '18 at 19:10
  • The width changes because your submenu items are wider than the parent menu items. The chosen answer actually contains a more elegant fix (rather than just the minimum change required to get it working). It adds an "active" class to set `max-height` when the submenu is displayed. I would set it to `auto` rather than `200px` but it's up to you. – todd Mar 23 '18 at 21:02
0

We add const subMenu = $(this).find('.submenu'); to find .submenu element with .menu > li selector.

Then we add our logic to toggle class via addClass and removeClass. I have also included second callback for .hover, which is hoverout.

$(selector).hover(hoverInCallback, hoverOutCallback);

Since I did not want to mess your css I have added only following rule

.submenu.visible { overflow: initial; max-height: auto; }

Hope this helps!

Here is working example.

$(document).ready(function () {

  $('.menu > li').hover(function() {
      const subMenu = $(this).find('.submenu');
      
      subMenu.addClass('visible');
  },
  // hoverout
  function() {
      const subMenu = $(this).find('.submenu');
      
      subMenu.removeClass('visible');
  });
  
});
nav {
    float: left;
    width: 200 px;
    position: fixed;
    top: 80px;
    left: 10px;
    border-top: 1px solid #9AA2B2;
    border-right: 1px solid #9AA2B2;
    border-left: 1px solid #9AA2B2;
}
.menu, .submenu {
  list-style: none;
  padding: 0;
  margin: 0;
}

.menu a {
    display: block;
    padding: 10px 15px;
    background: linear-gradient(to bottom, #7d7e7d 0%,#6B737E 100%); 
    border-top: 1px solid #9AA2B2;
    border-bottom: 1px solid #2E323A;
    text-decoration: none;
    color: white;
}

.menu a:hover {

    background: linear-gradient(to bottom, #3EB9E7 0%,#8abbd7 100%); 
}


.submenu a {

    padding: 10px 25px;
    background: linear-gradient(to bottom, #E2E2E2 0%,#fcfff4 100%);
    border-bottom: 1px solid #CFD0CF;
    color: black;
}

.submenu a:hover {

    background: linear-gradient(to bottom, #C5C6C5 0%,#d6dbbf 100%); 
  background-color: #C5C6C5;
}

.submenu {
  overflow: hidden;
  max-height: 0;
}

.submenu.visible {
  overflow: initial;
  max-height: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav>
  <ul class="menu">
    <li><a href="">Home</a></li>
    <li><a href="">Page 1</a></li>
    <li><a href="">Page 2</a>
      <ul class="submenu">
        <li><a href="">Subpage 2a</a></li>
        <li><a href="">Subpage 2b</a></li>
        <li><a href="">Subpage 2c</a></li>
      </ul>
    </li>
    <li><a href="">Page 3</a></li>
  </ul>
</nav>
loelsonk
  • 3,570
  • 2
  • 16
  • 23