Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery hover dependent on two elements

I have main navigation and sub navigation that for reasons of design are in separate DIVs. I would like to show the appropriate sub-nav when a main nav item is hovered, which I can do, but I also want to keep the sub-nav open if the user moves their mouse outside of the main nav item and into the sub-nav area. The last part is the place where I'm getting stuck.

I'm thinking on the hover out I need to do something with setTimeout() and an IF statement, but I have not been able to make any progress in that area. Is that even an approach worth trying?

HTML:

<div id="mnav">
 <ul id="buttons">
  <li class="one"><a href="#">Main 1</a></li>
  <li class="two"><a href="#">Main 2</a></li>
  <li class="three"><a href="#">Main 3</a></li>
  <li class="four nav-dark"><a href="#">Main 4</a></li>
 </ul>
</div> <!-- /mnav -->

<div id="snav">
 <ul class="snav-one">
    <li><a href="#">Sub 1.1</a></li>
    <li><a href="#">Sub 1.2</a></li>
    <li><a href="#">Sub 1.3</a></li>
    <li><a href="#">Sub 1.4</a></li>
    <li><a href="#">Sub 1.5</a></li>
    <li><a href="#">Sub 1.6</a></li>
 </ul>
 <ul class="snav-two">
    <li><a href="#">Sub 2.1</a></li>
    <li><a href="#">Sub 2.2</a></li>
 </ul>
</div> <!-- /snav -->

JS:

$(document).ready(function() {
 $("#buttons li.one, #buttons li.two").hover(function(){
  var subnav = 'ul.snav-' + $(this).attr('class');

  $("#snav").slideDown('fast').addClass("open").find(subnav).show();

 }, function(e){
  var subnav = 'ul.snav-' + $(this).attr('class');

  $("#snav").slideUp('fast').removeClass("open").find(subnav).hide();
 });
});
like image 449
creativetim Avatar asked May 25 '11 23:05

creativetim


3 Answers

For Mouse-menu ergonomics you want a small delay while mousing from main to sub menus, so the the sub menu does not close before the mouse gets there. (As the question says.)

But, you also need a delay before menus open -- both to avoid annoying "flyover" activation, and to reduce the common occurrence of accidentally switching from sub1 to sub2 while moving off the main menu.

So, the question code needs:

  1. hover over the sub-menu ul elements.
  2. stop to halt running animations if mouse selection changes.
  3. A resettable timer controlling both open and close.

See the demo at jsFiddle.

Putting it all together:

$("#buttons li.one, #buttons li.two").hover ( function () { MenuOpenCloseErgoTimer (
        100,
        function (node) {
            var subnav = 'ul.snav-' + $(node).attr ('class');
            $("#snav ul").hide ();
            $("#snav").stop (true, true).slideDown ('fast').addClass ("open").find (subnav).show ();
        },
        this
    ); },
    function () { MenuOpenCloseErgoTimer (
        200,
        function () {
            $("#snav").stop (true, true).slideUp ('fast').removeClass ("open").find ('ul').hide ();
        }
    ); }
);

$("div#snav ul").hover ( function () { MenuOpenCloseErgoTimer (
        0,
        function () {
            $("#snav").stop (true, true).slideDown ('fast').addClass ("open");
            $(this).show ();
        }
    ); },
    function () { MenuOpenCloseErgoTimer (
        200,
        function () {
            $("#snav").stop (true, true).slideUp ('fast').removeClass ("open");
            $("#snav ul").hide ();
        }
    ); }
);

function MenuOpenCloseErgoTimer (dDelay, fActionFunction, node) {
    if (typeof this.delayTimer == "number") {
        clearTimeout (this.delayTimer);
        this.delayTimer = '';
    }
    if (node)
        this.delayTimer     = setTimeout (function() { fActionFunction (node); }, dDelay);
    else
        this.delayTimer     = setTimeout (function() { fActionFunction (); }, dDelay);
}



Note the extra operations required on #snav ul, to cleanup after interrupted swaps between sub-menus.

like image 69
Brock Adams Avatar answered Nov 02 '22 23:11

Brock Adams


See example →

The following should work for you, I made several changes:

  • used the .stop() method to halt the animation on entering the subnav
  • moved the slides to the subnav ul's instead of the container so the above would work
  • some other things

See below:

$("#buttons li.one, #buttons li.two").hover(function() {
    var subnav = 'ul.snav-' + $(this).attr('class');

    $("#snav").find('ul').slideUp('fast');
    $("#snav").addClass("open").find(subnav).stop(true, true).slideDown('fast');
}, function(e) {
    var subnav = 'ul.snav-' + $(this).attr('class');

    $("#snav").removeClass("open").find(subnav).slideUp('fast');
});

$('#snav').bind('mouseenter', function(e) {
    $(this).find('ul').stop(true, false);
}).bind('mouseleave', function(e) {
    $(this).find('ul').stop(true, true).slideUp('fast');
});

See example →

like image 37
mVChr Avatar answered Nov 02 '22 23:11

mVChr


Instead of using the .hover() method, try using separate event handlers for mouseenter and mouseleave. The mouseenter would only be attached to the mnav buttons, while the mouseleave would be attached to both the mnav button and the snav div. On your mouseleave functions you'll probably have to add a small timeout and check to see if they went from one element to another.

Try something like this:

<script>
    var timer;
    $(document).ready(function() {
        $("#mnav").mouseenter(function() {
            $("#snav").slideDown();
        });

        $("#mnav, #snav").mouseleave(function() {
            timer=setTimeout("$('#snav').slideUp();",50);
        });

        $("#mnav, #snav").mouseenter(function() {
            clearTimeout(timer);
        });
    });
</script>
like image 38
T Nguyen Avatar answered Nov 03 '22 00:11

T Nguyen