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();
});
});
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:
hover
over the sub-menu ul
elements.
stop
to halt running animations if mouse selection changes.
- 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.
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>
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 →