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

IT技术 javascript dom-events
2021-03-16 21:05:16

我需要知道鼠标光标何时离开div. 所以我连接了这个mouseout事件。但是,如果我非常快速地将鼠标移出 ,则不会触发divmouseout事件这是正确的:鼠标光标坐以待毙里面,它现在外面,然而在回调没有被调用。(如果我不那么快地移动鼠标,它就可以正常工作。)divdivmouseout

顺便说一下,在最新的谷歌浏览器中确实如此——所以不仅仅是“旧浏览器”的问题。

解决方法:

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

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

关于我的问题...

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

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

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

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

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

6个回答

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

或者,如果找到元素,您可以沿着 parentNode 树向上并停止。然后你知道你在元素里面并且仍然在里面,否则你到达文档并且你在外面。

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

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

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

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

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

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

    与让浏览器本机完成所有这些相比,对每个像素移动执行计算有点性能损失。

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

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

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


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

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

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

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

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

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

(旁白:我的 JQuery 代码需要使用较旧的 jQuery 方法,因为它必须在运行 jQuery 1.4 的 Drupal 7 中运行,此时不应用即将推出的补丁)。

我遇到了这个问题几次,我开始接受这个问题作为生活的事实。但是根据您的需要,您可以像我一样使用 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