如何使用绝对定位的非后代实现 mouseleave 效果?

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

标准 mouseout 事件的一个问题是,它不仅会在光标离开元素外部边界所界定的屏幕区域时触发,还会在光标悬停在其中包含的其他元素上时触发这个周长。

jQuery 的 mouseleave 事件的基本原理是 仅在 光标离开由元素的外部边界界定的区域时发出信号.

不幸的是,这似乎只有在 "obstructing" 元素是 "obstructed" 元素的后代时才有效。如果 "obstructing" 元素通过绝对定位位于它所在的位置,则当鼠标悬停在其上时,将触发 "obstructed" 元素上的 mouseleave 事件。

例如,使用以下 HTML:

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

...#d-div#b-div 的真正后代,而 #c-div 不是,但是,但是我们可以对其进行样式设置,使其 "obstructs" #b-div都一样。 this jsFiddle.

对此进行了说明

如果现在在 #b-div 上定义以下事件:

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

...然后将鼠标悬停在 #b-div 的外围会导致该外围出现蓝色轮廓,除非 鼠标悬停在 #c-div.

有没有办法用 #b-div#c-div 获得与 mouseleave#b-div#d-div 达到相同的效果?

编辑:我已经修复了 jsFiddle 中显示的示例。此示例的原始版本显示了不具代表性的特殊情况,其中所有障碍元素都与障碍元素重叠。在这种特殊情况下,可以通过在阻塞元素和被阻塞元素上定义相同的事件来 模拟 所需的效果,从而实际上将阻塞元素变成被阻塞元素的一个补丁元素。当阻碍元素没有完全包含在被阻碍元素的外围时,这将不起作用(如修改后的 jsFiddle 所示)。更一般地说,任何基于在阻碍元素上使用 mouseover 事件的解决方案都注定会失败,因为 真实 问题是为了防止(或使无效)虚假mouseleave 在被阻挡的元素上。

这样做是基于您最初的 post,其中 #c-div 完全包含在 #b-div 中:

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

Fiddle 1


由于 #c-div 可能并不总是完全包含在 #b-div 中,如果您添加此样式,则可以使用现有代码:

#c-div {
  pointer-events: none;
}

但这将无法使用鼠标与 #c-div 交互。

Fiddle 2


如果您 需要与 #c-div 进行交互,则它不完全在 #b-div 内,您可以像这样使用 Element.getBoundingClientRect

$('#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

如果您无法使用 Rich 的 pointer-events: none 建议(也许您需要 support IE 10 or you do need to interact with the absolutely positioned div), you can manually check that the event is not going to #c-div using relatedTarget

但是,您还必须检查 #c-div 的 mouseleave 不会转到 #b-div

$( '#b-div' ).bind( {
    mouseenter: function () {
        $( this ).addClass( 'outlined' );
    },
    mouseleave: function (e) {
        if (e.relatedTarget.id == 'c-div' ||  $.contains(document.getElementById('c-div'), e.relatedTarget)) {
            return;
        }
        $( this ).removeClass( 'outlined' );
    }
} );

$( '#c-div' ).bind( {
    mouseleave: function (e) {
        if (e.relatedTarget.id == 'b-div' ||  $.contains(document.getElementById('b-div'), e.relatedTarget)) {
            return;
        }
        $( '#b-div' ).removeClass( 'outlined' );
    }
});
#a-div {
    position: relative;
    margin: 20px;
}
#b-div {
    height: 100px;
    width: 100px;
    background-color: #555;
    padding: 50px;
}
#c-div {
    position: absolute;
    height: 50px;
    width: 200px;
    top: 100px;
    left: 100px;
    background-color: #999;
}
#d-div {
    height: 50px;
    width: 50px;
    background-color: #ddd;
    text-align: center;
}
#c-div span {
    margin: 21.5px;
    line-height: 50px;
}
#d-div span {
    margin: auto;
    line-height: 50px;
}
.outlined {
    outline: 10px solid steelblue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="a-div">
    <div id="b-div"><div id="d-div"><span>d</span></div></div>
    <div id="c-div"><span>c</span></div>
</div>