Dimitri Vorontzov
Dimitri Vorontzov

Reputation: 8104

Keep submenu open on mouse out

A navigation menu I'm working on has a default CSS behavior (for those rare people who have JavaScript disabled). By default, the submenu is not displayed:

.main-navigation ul ul {
display:none;
}

On hover, the submenu is revealed:

.main-navigation ul li:hover > ul {
    display:block;  
}

For the JavaScript-minded majority, the menu is juiced up with the following jQuery snippet:

jQuery(document).ready(function($) {

/* cancel the default CSS hover behavior */

 $('.main-navigation ul li').on('mouseover',function(){
        $('.main-navigation ul li:hover > ul').css('display', 'none');
    $(this).css('cursor', 'pointer');
     });

/* toggle submenu display (if the submenu actually exists) */

   $('.main-navigation ul li a').click(function() { 
    var li = $(this).closest('li'); 
    if(li.has('ul')) li.find('ul').slideToggle(100); 
});

});

This toggling works great, except it only works as long as the mouse cursor stays over the parent link. If the submenu is open, and the user happens to move the mouse away from the parent link, the submenu snaps shut.

Question: How do I keep the submenu open on mouse out, if it's been already open?

I tried adding something like this to my jQuery snippet:

$('.main-navigation ul li').on('mouseout',function(){
    if ($('.main-navigation ul li ul').css('display') = 'none') {
    $('.main-navigation ul li ul').css('display', 'none');
} else if ($('.main-navigation ul li ul').css('display') = 'block') {
    $('.main-navigation ul li ul').css('display', 'block');
}
});

Not only it's mediocre coding, but it also actually doesn't work. ;-(

How should I fix this issue?

Thank you in advance for your suggestions!

Upvotes: 4

Views: 4729

Answers (2)

SpYk3HH
SpYk3HH

Reputation: 22570

i'm not sure the click issue yet (looking at it), but you don't need JavaScript to "disable" the CSS. Simply use <noscript> tags, like so:

<noscript>
    <style type="text/css">
        .exampleclass:hover { display: block; }
    </style>
</noscript>

Or you could simply add a no-js class to you main menu element, then remove that class if JS is enabled at the very start of your JavaScript. Then write your "no-js css" to use .no-js + whatever children instead of the main class.

UPDATE

The problem is simple, when you use mouseover to cancel your "non-js" css, the menu is still being hidden everytime the user hovers over that submenu. In other words, you're not just removing the "no js" css, you're hiding it on every mouseover of .main-navigation ul li!

Simply follow something in my first suggestion, then remove the mouseover function completely and viola! problem solved!

I wrote a jsFiddle using your code to show how I might approach it.

jsFiddle

Code

$(function() {
    //    See in css where i changed `.main-navigation ul li:hover > ul` to `.main-navigation.no-js ul li:hover > ul`
    //    See Also in HTML where i added class `no-js` to `#site-navigation`
    $(".no-js").removeClass("no-js");
    $('.main-navigation ul li a').on("click", function(e) {
        //    first hide sibling sub-menus!
        $(this).closest('li').siblings().each(function(i) { $(this).find("ul").slideUp("fast"); });
        //    no need for the if statement you had. 
        //    jQuery is "smart", if it doesn't exist, 
        //        then this function simply won't do anything!
        $(this).closest('li').find('ul').slideToggle(100); 
    })
    //    and just to add a little for ya, 
    //        the following will slideUp our submenu if user hovers away from MAIN MENU
    .closest("ul").on("mouseleave", function(e) {
        $(this).find("ul:visible").slideUp("slow");
    });
})

Step-by-Step

  1. Where you have manual script at between <script type="text/javascript"> tags, just before that noscript tage you threw in(which you can remove), replace all your JS with the following:

    <script type="text/javascript">
        jQuery(document).ready(function(jQuery) {
    
            jQuery(".no-js").removeClass("no-js");
            jQuery('.main-navigation ul li a').on("click", function(e) {
                $(this).closest('li').siblings().each(function(i) { $(this).find("ul").slideUp("fast"); });
                jQuery(this).closest('li').find('ul').slideToggle(100); 
            })
            //  If you find the menu hiding to fast, simply remove or comment out the next 3 lines
            jQuery('.main-navigation ul').on("mouseleave", function(e) {
                jQuery(this).find("ul:visible").slideUp("slow");
            });
    
        });
    </script>
    
  2. Remove the NOSCRIPT TAGS

  3. In your CSS Code:

    /* Find the area that was written as */
    .main-navigation ul li:hover > ul {
        display:block;  
    }
    
    /* And replace it with the following */
    .main-navigation.no-js ul li:hover > ul {
        display:block;  
    }
    
  4. Finally, look in your HTML, find the line written as <nav id="site-navigation" class="main-navigation" role="navigation"> and replace it with:

    <nav id="site-navigation" class="main-navigation no-js" role="navigation">
    

Upvotes: 1

DefyGravity
DefyGravity

Reputation: 6011

so here is where IE did something neat, and jquery makes it browser agnostic so it's usable. mouseleave is 'mouseout' for the selected element and any of its subelements in IE, and jquery makes it work for the other browsers.

The mouseleave JavaScript event is proprietary to Internet Explorer. Because of the event's general utility, jQuery simulates this event so that it can be used regardless of browser. This event is sent to an element when the mouse pointer leaves the element. Any HTML element can receive this event.

mouseover - when someone mouses over the 'parent' ul li you want to show any sub uls

click - when someone clicks the parent ul li you want to hide or show any sub uls

mouseleave - IE specific that jquery makes browser agnostic for you.

leave the menus in a working state using <noscript> tags, and intend the javascript to go from there if it is available.

fiddle -- this fiddle is just to give you a start, as i didn't put in any of your css.

$(function () {
    $("ul").on({"mouseover":function(event){
        $(this).find("ul").show("slow");
    }},"li.menu-item",null).on({"click":function(event){
        $(this).find("ul").toggle("slow");
    }},null,null).on({"mouseleave":function(event){
        $(this).find("ul").hide("slow");
    }},null,null);
});

Upvotes: 1

Related Questions