mrN
mrN

Reputation: 3770

Prevent accidental event bubbling up on element

I am trying to create a simple hover effect on a list elements. But, there is small gap between the element that shows up when mouse is over the list item. This forces the event mouseleave to be triggered, which is unwanted.

Here is fiddle demo. Try to go to the small box after hovering over the list item.

HTML:

<ul>
    <li>test <div class="block">Block</div></li>
    <li>test2 <div class="block">Block descriptions</div></li>
</ul>​

CSS:

ul { width: 50px; }
ul li { width: 50px; height: 50px; background: #c00; margin: 0 0 10px 0; }
li .block { 
    position: absolute; 
    margin: 0 0 0 70px;
    background: #ddd;
    display: none;
}

JavaScript:

$("ul li").hover(function() {
    $(this).children(".block").stop().fadeIn();
}, function() {
    $(this).children(".block").stop().fadeOut();
});​

How to prevent such effect?

Upvotes: 1

Views: 824

Answers (4)

Roko C. Buljan
Roko C. Buljan

Reputation: 206353

Try this demo:

$('ul li').on("mouseenter mouseleave",function( e ){
   var $this = $(this);
   if (e.type == 'mouseenter') {
       clearTimeout( $this.data('timeout') );
       $this.find('.block').stop().fadeTo(400,1);
   }else{
       $this.data( 'timeout', setTimeout(function(){
           $this.find('.block').stop().fadeTo(200,0, function(){
             $(this).hide(); 
           });
       },200) );
   }
});

What it does is: set a timeout value to the data attribute of the currently hovered li element that will act like a hover intent that will wait 200ms for the mouseenter on your .block elements maintaining the element visible. Once the mouse reaches your block element it will reset the timeout.

Upvotes: 1

Felix Kling
Felix Kling

Reputation: 816790

The mouseleave event is triggered because you literally leave the element when you ty to move the mouse over to the child. The only solution is to extend the width of the li element.

The maintain the same look, I suggest to move the red box into its own element, for example:

<ul>
    <li>
        <span class="icon">test</span> 
        <span class="block">Block</span>
    </li>
    <li>
        <span class="icon">test2</span> 
        <span class="block">Block descriptions</span>
    </li>
</ul>​

And change the CSS to:

li span {
    display: inline-block;
    vertical-align: middle;
}

li .icon {
    width: 50px; 
    height: 50px; 
    background: #c00;
}

li .block { 
    margin: 0 0 0 70px;
    background: #ddd;
    display: none;
}

The li element extends over both children: DEMO. Note though that display: inline-block might not work in older IE versions. (should be fine according to http://caniuse.com/inline-block).

Upvotes: 0

adeneo
adeneo

Reputation: 318302

You have two elements with mouse event handlers attached, and they are placed with a little distance from each other, so when the mouse is leaving one element on the way to the other element it goes outside both element and the event handler is triggered again etc.

You can either place both elements inside a wrapper element and attach the event handler to that element

<ul>
    <li><div>test <div class="block">Block</div></div></li>
    <li><div>test2 <div class="block">Block descriptions</div></div></li>
</ul>​​​​​​​​​​​​​​​​​​​

js:

$("ul li").on({
    mouseenter: function() {
        $(".block", this).fadeIn();
    },
    mouseleave: function() {
        $(".block", this).fadeOut();
    }
});​

FIDDLE

or you can try to guesstimate the time it takes to move the mouse and use a little timeout to fake it:

var timer;

$("ul li").on({
    mouseenter: function() {
        clearTimeout(timer);
        $(this).children(".block").fadeIn();
    },
    mouseleave: function() {
        var self = this;
        timer = setTimeout(function() {
            $(self).children(".block").fadeOut();
        }, 400);
    }
});​

FIDDLE

Upvotes: 0

ek_ny
ek_ny

Reputation: 10243

something like this should work. might need a couple of tweaks.

http://jsfiddle.net/G5YfR/

var fn = function(elem) {
    $(elem).children(".block").fadeOut();
};
var timeout;
$("ul li").on("mouseenter", function() {
    var that = this;
    clearTimeout(timeout);
    $(this).children(".block").fadeIn();
}).on("mouseleave", function() {
    var that = this;

    timeout = setTimeout(function() {
        fn(that)
    }, 1000);
});

Upvotes: 0

Related Questions