Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to achieve mouseleave effect with absolutely-positioned non-descendants?

One problem with the standard mouseout event is that it fires not only when the cursor leaves the region of the screen bounded by the element's external perimeter, but also when the cursor hovers over some other element contained within this perimeter.

The rationale for jQuery's mouseleave event is to signal only the moment when the cursor leaves the the area bounded by an element's external perimeter.

Unfortunately, this seems to work only if the "obstructing" element is a descendant of the "obstructed" element. If the "obstructing" element is where it is through absolute positioning, then when the mouse hovers over it, the mouseleave event on the "obstructed" element gets fired.

For example, with the following HTML:

<div id="b-div">
    <div id="d-div"><span>d</span></div>
</div>
<div id="c-div"><span>c</span></div>

...#d-div is a bona-fide descendant of #b-div, whereas #c-div isn't, but, but we can style it so that it "obstructs" #b-div all the same. This is illustrated in this jsFiddle.

If now one defines the following events on #b-div:

$( '#b-div' ).bind( {
    mouseenter: function () {
        $( this ).addClass( 'outlined' );
    },
    mouseleave: function () {
        $( this ).removeClass( 'outlined' );
    }
} );

...then hovering the mouse within #b-div's outer perimeter causes a blue outline to appear over this perimeter, unless the mouse is over #c-div.

Is there a way to get the same effect with #b-div and #c-div as mouseleave achieves with #b-div and #d-div?

EDIT: I've fixed the example shown in the jsFiddle. The original version of this example showed the unrepresentative special case in which all of the obstructing element overlaps with the obstructed element. In this special case, the desired effect can be simulated by defining the same events on both the obstructing and the obstructed elements, thus, in effect, turning the obstructing element into a patch of the obstructed element. This won't work when the obstructing element is not fully contained within the obstructed element's outer perimeter (as shown in the amended jsFiddle). More generally, any solution that is based on using a mouseover event on the obstructing element is bound to fail, since the real problem is to prevent (or render ineffective) the spurious mouseleave on the obstructed element.

like image 897
kjo Avatar asked Jun 01 '15 18:06

kjo


People also ask

In what situation will the event Mouseleave will take effect trigger?

The mouse leave event will be triggered whenever the mouse cursor leaves from the selected element and after the occurrence of the event, it implements a mouse leave event that has been attached to an event handler function to run.

What is the difference between Mouseout and Mouseleave?

This means that mouseleave is fired when the pointer has exited the element and all of its descendants, whereas mouseout is fired when the pointer leaves the element or leaves one of the element's descendants (even if the pointer is still within the element).

What is Mouseleave?

The mouseleave event occurs when the mouse pointer leaves the selected element. The mouseleave() method triggers the mouseleave event, or attaches a function to run when a mouseleave event occurs. Note: Unlike the mouseout event, the mouseleave event only triggers when the mouse pointer leaves the selected elements.

What is the difference between mouseover and Mouseenter?

The mouseover event triggers when the mouse pointer enters the div element, and its child elements. The mouseenter event is only triggered when the mouse pointer enters the div element. The onmousemove event triggers every time the mouse pointer is moved over the div element.


1 Answers

This does it, based on your initial post in which #c-div was completely contained within #b-div:

$('#b-div, #c-div').on( {
  mouseenter: function (ev) {
    $('#b-div').addClass('outlined');
  },
  mouseleave: function (ev) {
    $('#b-div').removeClass('outlined');
  }
});

Fiddle 1


Since #c-div may not always be contained completely within #b-div, you can use your existing code if you add this style:
#c-div {
  pointer-events: none;
}

But this will make it impossible to interact with #c-div using the mouse.

Fiddle 2


If you do need to interact with #c-div, and it's not completely within #b-div, you can use Element.getBoundingClientRect like this:

$('#b-div, #c-div').on('mousemove mouseleave',
  function(ev) {
    var br= $('#b-div')[0].getBoundingClientRect();
    $('#b-div').toggleClass(
      'outlined',
      ev.pageX > br.left && ev.pageX < br.left+br.width &&
      ev.pageY > br.top  && ev.pageY < br.top +br.height
    )
  }
);

Fiddle 3

like image 154
Rick Hitchcock Avatar answered Sep 21 '22 10:09

Rick Hitchcock