如何用绝对定位的非后代实现鼠标离开效果

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

本文关键字:鼠标 实现 离开 后代 何用绝 定位      更新时间:2023-09-26

标准mouseout事件的一个问题是,它不仅在光标离开以元素外部边界为界的屏幕区域时触发,而且当光标悬停在包含在该边界内的其他元素上时也会触发。

jQuery的mouseleave事件的基本原理是,当光标离开元素的外部边界区域时,仅向发出信号。

不幸的是,这似乎只在"阻塞"元素是"阻塞"元素的后代时才有效。如果"阻塞"元素通过绝对定位处于其所在位置,则当鼠标悬停在其上时,"阻塞"元素上的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不是,但是,但是我们可以对它进行样式设置,以便它"阻碍"#b-div。这在这个 jsFiddle中有说明。

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

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

…然后将鼠标悬停在#b-div的外周长内,会在该周长上出现一个蓝色轮廓,除非鼠标在#c-div上。

是否有一种方法可以获得#b-div#c-divmouseleave实现#b-div#d-div相同的效果?

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

根据您的初始帖子#c-div完全包含在#b-div中:

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

小提琴1


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

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

但是这将使使用鼠标与#c-div交互变得不可能。

小提琴2


如果你需要与#c-div交互,它不完全在#b-div内,你可以使用元素。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
    )
  }
);

小提琴3

如果你不能使用Rich对pointer-events: none的建议(也许你需要支持IE 10或者你确实需要与绝对定位的div进行交互),你可以手动检查事件不会使用relatedTarget进入#c-div

但是,您还必须检查#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>