如何检测鼠标离开窗口?

139

我希望能够检测到鼠标离开窗口,以便在用户的鼠标移动到其他位置时停止事件触发。

有什么好的想法吗?


1
看看这个“beeker exit-intent” GitHub 库,很不错。https://github.com/beeker1121/exit-intent-popup - raksheetbhat
2
使用mouseleave来防止像mouseout一样在所有元素上触发:非常好的解释和支持: https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseleave_event - user985399
设置文档事件时,应该使用文档而不是document.body,以防止在滚动条上方触发事件。Windows滚动条似乎会缩小文档主体。 - user985399
19个回答

103

请记住我的答案已经过时。

在 HTML 页面上实现拖放行为通常需要这种类型的行为。以下解决方案已在 MS Windows XP 机器上的 IE 8.0.6、FireFox 3.6.6、Opera 10.53 和 Safari 4 上进行了测试。
首先是 Peter-Paul Koch 的一个小函数;用于跨浏览器事件处理程序:

function addEvent(obj, evt, fn) {
    if (obj.addEventListener) {
        obj.addEventListener(evt, fn, false);
    }
    else if (obj.attachEvent) {
        obj.attachEvent("on" + evt, fn);
    }
}

然后使用此方法将事件处理程序附加到文档对象的mouseout事件:

addEvent(document, "mouseout", function(e) {
    e = e ? e : window.event;
    var from = e.relatedTarget || e.toElement;
    if (!from || from.nodeName == "HTML") {
        // stop your drag event here
        // for now we can just use an alert
        alert("left window");
    }
});

最后,这是一个包含嵌入式调试脚本的HTML页面:

<html>
<head>
<script type="text/javascript">
function addEvent(obj, evt, fn) {
    if (obj.addEventListener) {
        obj.addEventListener(evt, fn, false);
    }
    else if (obj.attachEvent) {
        obj.attachEvent("on" + evt, fn);
    }
}
addEvent(window,"load",function(e) {
    addEvent(document, "mouseout", function(e) {
        e = e ? e : window.event;
        var from = e.relatedTarget || e.toElement;
        if (!from || from.nodeName == "HTML") {
            // stop your drag event here
            // for now we can just use an alert
            alert("left window");
        }
    });
});
</script>
</head>
<body></body>
</html>

在Chrome浏览器中,当鼠标未被按下时,似乎这个事件不会触发。但是当鼠标被按下时,它会触发。看起来行为不一致。 - antony.trupe
6
如果其他HTML元素填充了窗口,这种方法就无法起作用。 - Emmanuel
1
这个解决方案的问题在于,即使用户只是尝试使用滚动条,它也会警报左窗口。我已经在这里发布了解决此问题的方案(http://stackoverflow.com/q/33157067/1207443),但仍有一个问题需要解决(请参见链接)。 - JohnyFree
奇怪,它在Chrome 49中无法工作,当离开而没有按左或右按钮时,它可以工作。但是如果按住左键,它就不会触发。 - Hank X
1
使用mouseleave代替mouseout来防止在文档中的元素上触发。非常好的解释,并且在IE5.5上也可以工作... https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseleave_event - user985399
显示剩余4条评论

58
如果您正在使用jQuery,那么这个简短而简洁的代码怎么样 -
$(document).mouseleave(function () {
    console.log('out');
});

每当鼠标不在您希望的页面上时,此事件将被触发。只需更改函数以执行所需操作。

您还可以使用:

$(document).mouseenter(function () {
    console.log('in');
});

当鼠标再次进入页面时触发。

来源:https://stackoverflow.com/a/16029966/895724


mouseleave正常工作,但如果打开检查元素也会触发,如何防止?有什么想法吗? - Pankaj Verma
这种技术在Chrome 15到56版本(我的当前版本)中不稳定。但在Firefox中它运行得非常好。请参见:https://dev59.com/ums05IYBdhLWcg3wGuKd - Tremours
mouseleave 似乎触发的次数比它应该触发的要多。 - Alexander
1
如果窗口处于后台(浏览器未聚焦)并且鼠标进入窗口(Chrome 61/macOS10.11),它也会触发。 - CodeBrauer
在使用此功能时,请注意Chrome关于mouseleave的主要bug: https://bugs.chromium.org/p/chromium/issues/detail?id=798535 - Skeets
1
@Skeets(以及未来的读者)这个问题已经在2019年2月14日标记为已解决。 - Xandor

40

为了检测鼠标离开事件,而不考虑滚动条、自动完成字段或检查:

document.addEventListener("mouseleave", function(event){

  if(event.clientY <= 0 || event.clientX <= 0 || (event.clientX >= window.innerWidth || event.clientY >= window.innerHeight))
  {

     console.log("I'm out");

  }
});

条件说明:

event.clientY <= 0  is when the mouse leave from the top
event.clientX <= 0  is when the mouse leave from the left
event.clientX >= window.innerWidth is when the mouse leave from the right
event.clientY >= window.innerHeight is when the mouse leave from the bottom

========================编辑===============================

似乎在新版本的Firefox浏览器中,document.addEventListener(“mouseleave”)不会触发,mouseleave需要附加到像body或子元素这样的元素。

我建议使用相反的方式

document.body.addEventListener("mouseleave")

或者

window.addEventListener("mouseout")

有时候不会触发。似乎当点流中的一个点被光标移动到边框并且光标变为调整大小时,它会失败。 - Ivan Borshchov
@user3479125,您能否在我的代码片段上尝试一下,并查看在不添加if语句时问题是否仍然存在?它考虑了滚动条,但我还没有测试自动完成或检查。https://dev59.com/CnNA5IYBdhLWcg3wh-cC#56678357 - user985399
1
这在2021年4月最新的Chrome版本89.0.4389.114中有效。 - MrLewk

31

这个对我有用:

addEvent(document, 'mouseout', function(evt) {
  if (evt.toElement == null && evt.relatedTarget == null) {
    alert("left window");
  }
});

6
这是Joshua Mills的回答中定义的一个函数。 - superjos
4
在IE8中,relatedTarget是未定义的,在IE9+和Firefox中,toElement也是未定义的。因此,正确的判断方式是: if ((evt.relatedTarget === null) || (evt.toElement === null)) { - carlos
2
谓词可以更简洁,如 if (!evt.toElement && !evt.relatedTarget) - Pavel Ye

19

这里是一个 2021 年的答案:

您可以在 html 标签中使用 mouseleave(以及 mouseenter 来检测进入时)(已在 Chrome 91 和 Firefox 90 中进行了测试)

尝试在下面的代码片段中悬停进入和退出。

document.documentElement.addEventListener('mouseleave', () => console.log('out'))
document.documentElement.addEventListener('mouseenter', () => console.log('in'))


11

太棒了!支持IE5.5+...也可以这样使用:document.onmouseleave = function() { alert('You left!') }; 不要使用document.body,因为它会在滚动条上方触发并缩小body!不需要编写防止元素触发的代码。好的解释请参考:https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseleave_event - user985399
这似乎是唯一适用于Firefox和Chrome的有效解决方案。像document.documentElement.addEventListener('mouseleave', function() { console.log('OUT'); })document.body.addEventListener('mouseleave', function() { console.log('OUT'); })这样的脚本在Chrome 90.0.4430.93上无法正常工作。 - Brigo

6
也许这会帮助到一些后来者。 window.onblurdocument.mouseoutwindow.onblur 在以下情况下被触发:
  • 使用 Ctrl+TabCmd+Tab 切换到另一个窗口。
  • 您聚焦(不仅是鼠标悬停)在文档检查器上。
  • 您切换桌面。
基本上是在浏览器选项卡失去焦点的任何时候。
window.onblur = function(){
    console.log("Focus Out!")
}

document.mouseout被触发的情况:

  • 您将光标移到标题栏上。
  • 您使用 Ctrl+TabCmd+Tab 切换到另一个窗口。
  • 您将光标移动到文档检查器上。

基本上,无论何时您的光标离开了文档,都会触发该事件。

document.onmouseleave = function(){
    console.log("Mouse Out!")
}

基本上,每当浏览器标签失去焦点时。在IE旧版本中,“blur”事件是在另一个元素获得焦点时/之后DOM完成的,对于Chrome而言,它是在元素本身失去焦点时完成的。只是提一下。 - Milche Patern
聪明地使用window.onblur。+1 - Radvylf Programs

5

我尝试了上述所有方法,但是没有一个像预期的那样有效。经过一些研究,我发现e.relatedTarget是在鼠标离开窗口之前的html

所以...最终我得到了这个:


<s>document.body.addEventListener('mouseout', function(e) {
    if (e.relatedTarget === document.querySelector('html')) {
        console.log('We\'re OUT !');
    }
});</s>

请告诉我如果您发现任何问题或改进!

2019更新

(如用户1084282所发现)

document.body.addEventListener('mouseout', function(e) {
    if (!e.relatedTarget && !e.toElement) {
        console.log('We\'re OUT !');
    }
});

2
在Chrome上对我不起作用。当鼠标离开窗口时,relatedTarget为空。受@user1084282答案的启发,将其更改为:if(!e.relatedTarget && !e.toElement) { ... },然后它就可以工作了。 - user993683
我确认,如果你使用if(!e.relatedTarget && !e.toElement)而不是实际答案中的条件,它可以在Chrome、Firefox和IE Edge上正常工作。它可以检测鼠标离开文档主体。 - Haijerome
不要将事件绑定在document.body上,因为窗口滚动条可能会缩小它并在滚动条之上触发。而且,当你可以使用'mouseleave'时,为什么要在所有冒泡元素上触发'mouseout'呢? https://developer.mozilla.org/zh-CN/docs/Web/API/Element/mouseleave_event - user985399

5

以上所列答案均无法解决我的问题。我现在使用以下方法:

document.addEventListener('dragleave', function(e){

    var top = e.pageY;
    var right = document.body.clientWidth - e.pageX;
    var bottom = document.body.clientHeight - e.pageY;
    var left = e.pageX;

    if(top < 10 || right < 20 || bottom < 10 || left < 10){
        console.log('Mouse has moved out of window');
    }

});

我正在使用这种拖放文件上传小部件。它并不是绝对准确的,只有当鼠标离窗口边缘一定距离时才会被触发。


我相信如果鼠标移动足够快,有时候这个操作不会被触发。 - John Weisz
@JohnWeisz 是的,我认为你是对的。我已经使用这个脚本一段时间了,它运行良好。 - Emmanuel

4
我尝试了一个接一个的答案,最终找到了当时最好的答案:

https://dev59.com/CnNA5IYBdhLWcg3wh-cC#3187524

我跳过了旧版本浏览器,所以我将代码缩短以适应现代浏览器(IE9+)

    document.addEventListener("mouseout", function(e) {
        let t = e.relatedTarget || e.toElement;
        if (!t || t.nodeName == "HTML") {
          console.log("left window");
        }
    });

document.write("<br><br>PROBLEM<br><br><div>Mouseout trigg on HTML elements</div>")

在这里,您可以看到浏览器支持的情况。

我认为这很简短

但是问题仍然存在,因为"mouseout"会在文档中的所有元素上触发

为了防止它发生,请使用mouseleave(IE5.5+)。请参见链接中的良好解释。

以下代码可在不触发要在内部或外部的元素的情况下工作。也尝试在文档外拖放并释放。

var x = 0

document.addEventListener("mouseleave", function(e) { console.log(x++) 
})

document.write("<br><br>SOLUTION<br><br><div>Mouseleave do not trigg on HTML elements</div>")

您可以在任何HTML元素上设置事件。不要将事件设置在document.body上,因为窗口滚动条可能会缩小页面主体,并在鼠标指针位于滚动条上方但您想要滚动而不想触发mouseLeave事件时触发。请像示例中那样将其设置在document上。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接