A smart approach would be to add a hover intent to wait before triggering mouseleave:
$("#menu").find("li:has(ul)").on('mouseenter mouseleave',function( e ){
var $UL = $(this).find('ul');
if(e.type==='mouseenter'){
clearTimeout( $(this).data('wait') );
$UL.stop(1,1).slideDown();
}else{
$(this).data('wait', setTimeout(function(){
$UL.stop().slideUp();
},180) );
}
});
- instead of using
if ($(this).find("ul").length > 0) { just use: ("li:has(ul)")
to trigger your events only on li elements that has ul as children.
- add an event callback
e for the mouseenter mouseleave.
- if the
e event == mouseenter ..... else is mouseleave.
- Than clear the
data attribute called 'wait' and slideDown the children ul
- Now, leaving the original
li element to reach the 'distant' ul we have to cross over a white space (demo) that will usually trigger immediately the 'slideUp()', but we set a timeout counter inside that li's data attribute that will wait ~180ms before running.
- Reaching the 'distant'
ul element - beeing a children of the timeouted 'li' we clear the timeout (step 1) 'retaining' the mouseenter state.
use ~180ms or how much you think is needed to reach with the mouse the 'distant' UL element
eachto add event listeners$("#menu ul li ul:not(:empty)").mouseenter(function(){...}) .mouseleave(function(){...});