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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7448468/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 00:10:16  来源:igfitidea点击:

Why can't I reliably capture a mouseout event?

javascriptdom-events

提问by callum

I need to know when the mouse cursor leaves a div. So I hook up the mouseoutevent. However, if I move the mouse very quickly out of the div, the mouseoutevent doesn't fire. That's right: the mouse cursor wassitting still inside the div, it's now outsidethe div, and yet the mouseoutcallback hasn't been called. (It works fine if I don't move the mouse quite so fast.)

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

This is true in the latest Google Chromeby the way – so not just an "old browsers" problem.

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

A workaround:

解决方法:

A question about this problem has been posed before. Apparently it's just a fact of life, and the only workaround I've found is to manually monitor mousemoveevents, each time checking the cursor's x/y co-ordinates and seeing if they fall within the div's bounding box, so you have more chances to "notice" if the cursor is no longer inside it.

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

Compared to letting the browser do all this natively, performing calculations on every pixel move is a bit of a performance hit. It's also tedious to code.

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

On to my question...

关于我的问题...

Whycan't the browser can't reliably capture the mouseoutevent? If I can reliably tell when the mouse has left the divusing the above workaround, why can't the browser do it?

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

I understand (from the answer linked above) that JavaScript doesn't try to interpolate "frames". Say if you put a mousemovehandler on the document, and quickly move the mouse 200 pixels to the right in a perfect horizontal line, you might notget 200 mousemoveevents. A few will be missed. I don't have a problem with that.

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

But if some pixel movements are missed just as the mouse crosses the boundary of the div, why does it follow that the mouseoutevent should also be skipped? When the browser finally starts registering the mouse's position again (after a sudden fast movement), even if the mouse is now milesoutside the box, the point is that it used to be in the box and no longer is. So why doesn't it then fire the mouseout event then?

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

I just don't get why this would be a hard problem for the browser vendors to solve. (But I trust there might be a good reason, which I'm too stupid to think of.)

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

I'm posting this question mainly out of curiosity, but I'm hoping the answer might give some insight that could help me work around the problem more efficiently. Also, any alternative workarounds (which are faster than the one presented above) would be welcome.

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

回答by Rafael Raposo

I know that you don't want a workaround, but you don't need to check mouse's x/y to know if you are in or out an element. You could simply check the element from which the mousemove event was fired. If you put a mousemove on document, the event will fire from one of its children, and you can compare that element with your element to know if it is one of its descendants.

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

Or you could go up the parentNode tree and stop if you find your element. Then you know you are inside the element and still in it, otherwise you reach the document and you are out.

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

Some browsers implement the mouseenter/mouseleave events that, I've noticed, are more accurate than mouseout. Prototype and jQuery have a workaround for browsers that don't implement these new events. Mouseleave does not fire from an element's children, whereas mouseout does.

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

回答by jimbo

You describe moving the mouse very quickly. When you stop, is the pointer still within the page? That is, is your mouse pointer still hovering over some part of the visible web page?

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

If it has gone outside, then it's not necessarily clear what the browser should do. The mouseoutevent should have a relatedTargetproperty that targets what the mouse pointer has gone into. If the mouse pointer is already outside of the page area, there would be no related target to point to.

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

Put another way, when the mouse leaves the page area, the browser stops tracking it and stops reporting its position. If you move your mouse fast enough, from the browser's perspective, the mouse simply disappeared. It's not until you bring the mouse back into the bounding box of the viewable page that the browser knows where it is, and then triggers all appropriate movement-based actions (like mouseout).

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

回答by vol7ron

  1. Why can't the browser can't reliably capture the mouseout event? If I can reliably tell when the mouse has left the div using the above workaround, why can't the browser do it?

    I think you answered this one yourself when you said:

    Compared to letting the browser do all this natively, performing calculations on every pixel move is a bit of a performance hit.

    The browser does not interpolate between the frames, thus, as you stated it would demand a lot more resources and memory, which may be why it isn't "fixed".

  2. If some pixel movements are missed just as the mouse crosses the boundary of the div, why does it follow that the mouseout event should also be skipped? When the browser finally starts registering the mouse's position again (after a sudden fast movement), even if the mouse is now miles outside the box, the point is that it used to be in the box and no longer is. So why doesn't it then fire the mouseout event then?

    I don't know for sure, but I don't think it's a condition of "it was in and now it's out". Instead, it's whether it crosses that boundary (if MouseX - ElemOffsetX= 1). I agree, it doesn't make as much sense, but it could be because if you set the value to > 1it would trigger the event multiple times. Otherwise it would have to keep track of the events, which is not in JS nature, seeing how it just adds events asynch to a stack.

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

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

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

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

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

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



What you could try is using jQuery's mouseleave event. This does two things, which delays the firing of the event:

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

  1. It traverses the DOM tree to see if it truly left the element
  2. I think it implements a timeout call, which should solve the interpolation problem that you noticed.
  1. 它遍历 DOM 树以查看它是否真的离开了元素
  2. 我认为它实现了超时调用,这应该可以解决您注意到的插值问题。

回答by Tan Duong

I ran into this problem a few times and I came to accept the issue as a fact of life. But depend on your needs, you can just use CSS as I did. For example, if I just want to show/hide an element base on another element got hovered, then CSS is the way to go. Here is a working, reliable example:

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

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

回答by jahooma

Here's a simple workaround.

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

In your onMouseOver listener, you can add a 'mousemove' listener to the window:

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

Then you can check on each mouse move until the mouse is outside of your div (rootRef.current in our example).

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

回答by Bryan

I found your question and the lack of other clear answers useful because it told me that I had to create a workaround. Which I did using the ideas presented in your question and the other contributors.

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

I have same problem when I use jquery mouseleave elem.bind('mouseleave', data, mouseLeavesZone);

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

The problem is intermittent and may be related to a busy CPU on the client. Say, for example, the CPU is busy elsewhere when your mouse moves out of a div. Then it seems logical that this could be the cause of the bug. I agree; this should be fixed by the browser vendors.

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

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

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

(Aside: My JQuery code needs to use older jQuery methods because it has to run in Drupal 7 which is running jQuery 1.4, at this time and without applying patches that are coming).

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