为啥我不能可靠地捕获 mouseout 事件?

Posted

技术标签:

【中文标题】为啥我不能可靠地捕获 mouseout 事件?【英文标题】:Why can't I reliably capture a mouseout event?为什么我不能可靠地捕获 mouseout 事件? 【发布时间】:2011-11-18 21:26:35 【问题描述】:

我需要知道鼠标光标何时离开div。所以我连接了mouseout 事件。但是,如果我很快将鼠标移出divmouseout 事件不会触发。没错:鼠标光标 静止在div 内,现在div 之外,但尚未调用mouseout 回调。 (如果我不那么快地移动鼠标,它工作得很好。)

顺便说一句,在最新的 Google Chrome 中确实如此 - 所以不仅仅是“旧浏览器”的问题。

解决方法:

关于这个问题的一个问题是posed before。显然这只是生活中的事实,我发现的唯一解决方法是手动监视mousemove 事件,每次检查光标的 x/y 坐标并查看它们是否落在div 的边界框内,因此如果光标不再在其中,您就有更多机会“注意到”。

与让浏览器在本地完成所有这些操作相比,对每个像素移动执行计算有点影响性能。编码也很乏味。

关于我的问题...

为什么浏览器不能可靠地捕获mouseout 事件?如果我可以使用上述解决方法可靠地判断鼠标何时离开div,为什么浏览器不能这样做?

我了解(从上面链接的答案)javascript 不会尝试插入“帧”。假设您在document 上放置了一个mousemove 处理程序,并以完美的水平线将鼠标快速向右移动200 像素,您可能不会获得200 个mousemove 事件。会错过一些。我对此没有意见。

但是,如果在鼠标越过div 的边界时错过了一些像素移动,为什么还要跳过mouseout 事件呢?当浏览器终于​​再次开始注册鼠标的位置时(在突然快速移动之后),即使鼠标现在 miles 在框外,关键是它曾经在框内而不再是。那么为什么它不触发 mouseout 事件呢?

我只是不明白为什么这对浏览器供应商来说是一个难以解决的问题。 (但我相信可能有一个很好的理由,我太愚蠢了,无法想到。)

我发布这个问题主要是出于好奇,但我希望答案可以提供一些见解,帮助我更有效地解决问题。此外,欢迎使用任何替代解决方法(比上述解决方法更快)。

【问题讨论】:

这对您绝对没有帮助(因此是评论,而不是答案),但这种行为绝对听起来像一个错误。这也可能很明显,但要解决您的问题“为什么还应该跳过 mouseout 事件?”,他的答案是 它没有,因为它是非故意的行为。 有趣。这实际上对我有很大帮助——如果你是对的,那是一个错误。对我来说似乎很奇怪,这种错误会存在于 Chrome 15 中!我将制作一个测试页面并在其他浏览器中尝试它,看看它的普及程度。我可能会向 crbug.com 提交一份错误报告,看看他们是怎么做的。谢谢。 仅供参考:Chrome 15 尚未进入稳定通道 @Ingenu:是的,不好的例子。但 Chrome 11-14(可能还有更早的版本)也是如此。我将看看其他浏览器是否也是这种情况(我必须先创建一个新的测试页面,因为我的示例是在 Chrome 扩展程序中)。 使用setInterval 是一种始终有效的解决方法(尽管这不是最好的)。无论触发什么事件或浏览器有什么怪癖,您都可以简单地每隔几毫秒检查一次。 【参考方案1】:

我知道您不想要解决方法,但您无需检查鼠标的 x/y 即可知道您是否在某个元素中。您可以简单地检查触发 mousemove 事件的元素。如果您在文档上放置鼠标移动,该事件将从其子元素之一触发,您可以将该元素与您的元素进行比较以了解它是否是其后代之一。

或者,如果你找到你的元素,你可以向上 parentNode 树并停止。然后你知道你在元素内部并且仍然在其中,否则你到达文档并且你出去了。

有些浏览器实现了 mouseenter/mouseleave 事件,我注意到,这些事件比 mouseout 更准确。 Prototype 和 jQuery 为不实现这些新事件的浏览器提供了一种解决方法。 Mouseleave 不会从元素的子元素触发,而 mouseout 会。

【讨论】:

【参考方案2】:

您描述了非常快速地移动鼠标。停止时,指针是否仍在页面内?也就是说,您的鼠标指针是否仍然悬停在可见网页的某些部分上?

如果它已经走出去,那么浏览器应该做什么就不一定清楚了。 mouseout 事件应该有一个 relatedTarget 属性,该属性以鼠标指针进入的内容为目标。如果鼠标指针已经在页面区域之外,就没有相关的目标可以指向了。

换句话说,当鼠标离开页面区域时,浏览器停止跟踪它并停止报告它的位置。如果您移动鼠标的速度足够快,从浏览器的角度来看,鼠标就会消失。直到您将鼠标移回可查看页面的边界框后,浏览器才知道它在哪里,然后触发所有适当的基于移动的操作(例如 mouseout)。

【讨论】:

【参考方案3】:

    为什么浏览器不能可靠地捕获 mouseout 事件?如果我可以使用上述解决方法可靠地判断鼠标何时离开 div,为什么浏览器不能这样做?

    我想你自己回答了这个问题,当你说:

    与让浏览器在本地完成所有这些操作相比,对每个像素移动执行计算有点影响性能。

    浏览器不会在帧之间进行插值,因此,正如您所说,它需要更多的资源和内存,这可能是它没有“固定”的原因。

    如果在鼠标越过 div 的边界时错过了一些像素移动,为什么还要跳过 mouseout 事件?当浏览器最终再次开始注册鼠标的位置时(在突然快速移动之后),即使鼠标现在在盒子之外几英里,关键是它曾经在盒子里,现在不在了。那么为什么不触发 mouseout 事件呢?

    我不确定,但我认为这不是“它在里面,现在它在外面”的条件。相反,它是否跨越了那个边界(如果MouseX - ElemOffsetX= 1)。我同意,这没有多大意义,但这可能是因为如果您将值设置为> 1,它将多次触发该事件。否则它必须跟踪事件,这不是 JS 的本质,看看它是如何将事件异步添加到堆栈中的。


您可以尝试使用jQuery's mouseleave event。这做了两件事,延迟了事件的触发:

    它会遍历 DOM 树,看看它是否真的离开了元素 我认为它实现了超时调用,这应该可以解决您注意到的插值问题。

【讨论】:

【参考方案4】:

我发现您的问题以及缺乏其他明确答案很有用,因为它告诉我必须创建一个解决方法。我使用您的问题和其他贡献者中提出的想法来做到这一点。

我在使用 jquery mouseleave elem.bind('mouseleave', data, mouseLeavesZon​​e); 时遇到同样的问题;

此问题是间歇性的,可能与客户端 CPU 繁忙有关。例如,当您的鼠标移出 div 时,CPU 正忙于其他地方。那么这可能是错误的原因似乎是合乎逻辑的。我同意;这应该由浏览器供应商解决。

见http://jsfiddle.net/bgil2012/gWP5x/1/

(顺便说一句:我的 JQuery 代码需要使用旧的 jQuery 方法,因为它必须在运行 jQuery 1.4 的 Drupal 7 中运行,此时并且不应用即将到来的补丁)。

【讨论】:

【参考方案5】:

我几次遇到这个问题,我开始接受这个问题作为生活中的事实。但是根据您的需要,您可以像我一样使用 CSS。例如,如果我只想根据悬停的另一个元素显示/隐藏一个元素,那么 CSS 就是要走的路。这是一个有效的可靠示例:

.large 
  width: 175px; height: 175px;
  position: absolute;
  border-radius: 100%;

  /*hide the glass by default*/
  top: -9999px;
  left: -9999px;
  opacity: 0;
  transition: opacity .2s ease-in-out;
  z-index: 100;
  pointer-events: none;


.small:hover + .large 
  opacity: 1;

http://codepen.io/tanduong/pen/aBMxyd

【讨论】:

【参考方案6】:

这是一个简单的解决方法。

在您的 onMouseOver 监听器中,您可以向窗口添加一个“mousemove”监听器:

<div onMouseOver=() => 
    setMouseOver(true)

    let checkMouseLeave = (e: MouseEvent) => 
        if (rootRef.current
            && !rootRef.current.contains(e.target as htmlElement)) 
            setMouseOver(false)
            window.removeEventListener('mousemove', checkMouseLeave)
        
    

    window.addEventListener('mousemove', checkMouseLeave)

></div>

然后您可以检查每个鼠标移动,直到鼠标位于您的 div 之外(在我们的示例中为 rootRef.current)。

【讨论】:

以上是关于为啥我不能可靠地捕获 mouseout 事件?的主要内容,如果未能解决你的问题,请参考以下文章

如何从服务中可靠地捕获 Windows 登录、注销、锁定和解锁事件?

如何解决鼠标移动到子元素 会触发父元素的mouseout事件

为啥把鼠标移到div里的超链接上,就触发了这个div的mouseout事件呢?

为啥在异步事件循环运行时我无法捕获 SIGINT?

在jQuery或Javascript中切换事件捕获?

当元素被覆盖时取消 mouseout 事件