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

时间:2011-09-16 17:45:42

标签: javascript javascript-events

我需要知道鼠标光标何时离开div。所以我联系了mouseout事件。但是,如果我将鼠标快速移出div,则mouseout事件不会触发。这是正确的:鼠标光标 仍然位于div内,它现在外面 div,而mouseout回调没有被称为。 (如果我不快速移动鼠标,它可以正常工作。)

顺便提一下,最新的谷歌浏览器也是如此 - 所以不仅仅是“旧浏览器”问题。

解决方法:

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

与让浏览器本身完成所有这些操作相比,对每个像素移动执行计算都会受到一点性能影响。编码也很繁琐。

关于我的问题......

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

我理解(从上面链接的答案)JavaScript不会尝试插入“框架”。假设您在mousemove上放置document处理程序,并快速将鼠标移动到完美水平线的右侧200像素,您可能获得200 {{1}事件。一些将被遗漏。我对此没有任何问题。

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

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

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

6 个答案:

答案 0 :(得分:7)

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

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

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

答案 1 :(得分:4)

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

如果它已经消失,那么浏览器应该做什么并不一定清楚。 mouseout事件应该具有relatedTarget属性,该属性针对鼠标指针所进入的内容。如果鼠标指针已经在页面区域之外,则没有相关的目标指向。

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

答案 2 :(得分:1)

  1.   

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

    我想你在说:

    时自己回答了这个问题
      

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

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

  2.   

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

    我不确定,但我不认为这是“它已经存在且现在已经存在”的条件。相反,它是否跨越该边界(如果MouseX - ElemOffsetX= 1)。我同意,它没有那么多意义,但可能是因为如果你将值设置为> 1,它会多次触发事件。否则,它必须跟踪不属于JS性质的事件,看它是如何将事件asynch添加到堆栈中。


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

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

答案 3 :(得分:1)

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

答案 4 :(得分:0)

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

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

问题是间歇性的,可能与客户端上繁忙的CPU有关。比如说,当你的鼠标离开div时,CPU正在其他地方忙碌。那么这可能是导致bug的原因似乎是合乎逻辑的。我同意;这应该由浏览器供应商修复。

请参阅http://jsfiddle.net/bgil2012/gWP5x/1/

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

答案 5 :(得分:0)

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

在您的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)。