136

I want to be able to detect when the mouse leaves the window so I can stop events from firing while the user's mouse is elsewhere.

Any ideas of how to do this?

  • 1
    Check out this beeker exit-intent github library. It's great. https://github.com/beeker1121/exit-intent-popup – raksheetbhat May 16 '18 at 08:44
  • 2
    Use **mouseleave** to prevent firing on all elements as the **mouseout** does: Very good explanation and support: https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseleave_event –  Jun 20 '19 at 13:34
  • Set event on document - not document.body to prevent fire abowe scroll-bar. Windows scrollbar seem to shrink the body. –  Jun 23 '19 at 00:50

19 Answers19

103

Please keep in mind that my answer has aged a lot.

This type of behavior is usually desired while implementing drag-drop behavior on an html page. The solution below was tested on IE 8.0.6, FireFox 3.6.6, Opera 10.53, and Safari 4 on an MS Windows XP machine.
First a little function from Peter-Paul Koch; cross browser event handler:

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

And then use this method to attach an event handler to the document objects mouseout event:

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");
    }
});

Finally, here is an html page with the script embedded for debugging:

<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>
J Mills
  • 1,730
  • 1
  • 19
  • 16
  • Using window.event fixed my problem getting this event. Thanks! – Jay Sep 03 '10 at 16:03
  • it looks like this doesn't fire when the mouse is not pressed in chrome. it does fire when the mouse is pressed in chrome. seems like inconsistent behavior. – antony.trupe Jan 27 '14 at 03:16
  • 6
    This doesn't work if other HTML elements are filling the window. – Emmanuel Dec 12 '14 at 01:07
  • 1
    The problem with this solution is that it alert left window even if user just try to use scroller. I published solution to this problem [here](http://stackoverflow.com/q/33157067/1207443), but there is still one problem needs to be solved (see the link). – JohnyFree Oct 17 '15 at 08:51
  • weird, it's not work in Chrome 49, when leaving without press left or right button, it works. but if hold the left button, it won't fired. – Hank X Dec 24 '15 at 14:40
  • I found that when you use margin at the top of your page, it reads that as exiting the window.. So useing padding instead fixes this! – nclsvh Sep 07 '16 at 14:08
  • Second thing I found is that when I enter from the top of the browser (mouse goes to the address-bar or above and back to the 'browser window') it works like intended. When I enter the page from the left (so mouse goes to dekstop and back in the browser 'window') it doesn't see that I entered back in the window. Any fixes for this? – nclsvh Sep 07 '16 at 14:10
  • 1
    Use **mouseleave** instead of mouseout to prevent fire on elements in document. Very good explanation and it works on IE5.5 ... https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseleave_event –  Jun 20 '19 at 13:24
  • This is what I would call over-engineered. Not to be _that_ person, but uhhh see _my_ answer. – Samie Bencherif Feb 26 '20 at 22:08
56

If you are using jQuery then how about this short and sweet code -

$(document).mouseleave(function () {
    console.log('out');
});

This event will trigger whenever the mouse is not in your page as you want. Just change the function to do whatever you want.

And you could also use:

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

To trigger when the mouse enters back to the page again.

Source: https://stackoverflow.com/a/16029966/895724

Community
  • 1
  • 1
Mandeep Janjua
  • 15,583
  • 4
  • 29
  • 24
  • mouseleave is working but it is also firing if inspect element is opened, how to prevent? Idea? – Pankaj Verma Jan 07 '17 at 13:01
  • This technique is not stable in Chrome 15 to 56 (my current version). But it works very will in Firefox. See: http://stackoverflow.com/questions/7448468/why-cant-i-reliably-capture-a-mouseout-event – Tremours Mar 07 '17 at 01:00
  • mouseleave seems to fire a lot more than it should. – Alexander Oct 30 '17 at 02:51
  • 1
    It also fires if the window is in background (browser not focused) and the mouse enters the window (Chrome 61/macOS10.11) – CodeBrauer Nov 03 '17 at 14:06
  • When using this, be aware that chrome has a *major bug* concerning mouseleave: https://bugs.chromium.org/p/chromium/issues/detail?id=798535 – Skeets Aug 31 '18 at 09:31
  • 1
    @Skeets (and future readers) This issue has been marked fixed as of Feb 14, 2019 – Xandor Aug 31 '19 at 15:36
40

In order to detect mouseleave without taking in account the scroll bar and the autcomplete field or inspect :

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");

  }
});

Conditions explanations:

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

======================== EDIT ===============================

document.addEventListener("mouseleave") seems to be not fired on new firefox version, mouseleave need to be attached to an element like body, or a child element.

I suggest to use instead

document.body.addEventListener("mouseleave")

Or

window.addEventListener("mouseout")
Daphoque
  • 4,421
  • 1
  • 20
  • 31
  • from time to time not fired. Seams like it fails when one point of points stream triggered by cursor moving aims border and cursor becomes resize. – Ivan Borshchov Sep 23 '19 at 19:43
  • @user3479125, could you try it out on my snippet also and see if the problem remains when the if-statement is not added? It takes in account the scroll bar but I have not tested with autocomplete or inspect. https://stackoverflow.com/a/56678357/985399 –  Oct 29 '19 at 13:10
  • 1
    This works in the latest Chrome Version 89.0.4389.114 in April 2021 – MrLewk Apr 08 '21 at 11:19
31

This works for me:

addEvent(document, 'mouseout', function(evt) {
  if (evt.toElement == null && evt.relatedTarget == null) {
    alert("left window");
  }
});
user1084282
  • 1,003
  • 7
  • 6
  • 6
    it's a function defined in [Joshua Mills answer](http://stackoverflow.com/a/3187524/540776) – superjos Aug 28 '12 at 00:00
  • 4
    on ie8, relatedTarget is undefined, on ie9+ and firefox, toElement is undefined. Because it, the correct evaluation is: if ((evt.relatedTarget === null) || (evt.toElement === null)) { – carlos Jul 15 '15 at 16:59
  • 2
    The predicate could be more concise as in `if (!evt.toElement && !evt.relatedTarget)` – Pa Ye Mar 21 '19 at 17:12
18

Here is a 2021 answer:

You can use mouseleave (and mouseenter to detect when entering) in the html tag (tested in Chrome 91 and Firefox 90)

Try in Snippet below, by hovering in and out of it.

document.documentElement.addEventListener('mouseleave', () => console.log('out'))
document.documentElement.addEventListener('mouseenter', () => console.log('in'))
dippas
  • 58,591
  • 15
  • 114
  • 126
10

Using the onMouseLeave event prevents bubbling and allows you to easily detect when the mouse leaves the browser window.

<html onmouseleave="alert('You left!')"></html>

http://www.w3schools.com/jsref/event_onmouseleave.asp

Samie Bencherif
  • 1,285
  • 12
  • 27
  • Fantastic! Support is IE5.5+ ... Works also: `document.onmouseleave = function() { alert('You left!') };` Use not document.body that fires abowe scroll bars that shrink body! Code to prevent fire on elements is not needed. Good explanation: https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseleave_event –  Jun 23 '19 at 01:00
  • This seems to be the only working solution both for Firefox and Chrome. Scripts like `document.documentElement.addEventListener('mouseleave', function() { console.log('OUT'); })` or `document.body.addEventListener('mouseleave', function() { console.log('OUT'); })` are not working on Chrome 90.0.4430.93 – Brigo May 05 '21 at 06:16
6

Maybe this would help some of those coming here later. window.onblur and document.mouseout.

window.onblur is triggered when:

  • You switch to another window using Ctrl+Tab or Cmd+Tab.
  • You focus (not just mouseover) on the document inspector.
  • You switch desktops.

Basically anytime that browser tab loses focus.

window.onblur = function(){
    console.log("Focus Out!")
}

document.mouseout is triggered when:

  • You move the cursor onto the title bar.
  • You switch to another window using Ctrl+Tab or Cmd+Tab.
  • You open move the cursor over to the document inspector.

Basically in any case when your cursor leaves the document.

document.onmouseleave = function(){
    console.log("Mouse Out!")
}
dippas
  • 58,591
  • 15
  • 114
  • 126
Jayant Bhawal
  • 2,044
  • 2
  • 31
  • 32
  • "asically anytime THAT browser tab loses focus." .. with IE older versions, the 'blur' event is DOM compoled when/after another element recieves the focus, for gooChrome, it is when the element itself looses the focus .. just to mention. – Milche Patern Jul 09 '16 at 06:29
  • Smart using `window.onblur` too. +1 – Radvylf Programs Sep 27 '19 at 17:14
5

None of these answers worked for me. I'm now using:

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');
    }

});

I'm using this for a drag and drop file uploading widget. It's not absolutely accurate, being triggered when the mouse gets to a certain distance from the edge of the window.

Emmanuel
  • 4,933
  • 5
  • 46
  • 71
5

I've tried all the above, but nothing seems to work as expected. After a little research I found that e.relatedTarget is the html just before the mouse exits the window.

So ... I've end up with this:


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

Please let me know if you find any issues or improvements !

2019 Update

(as user1084282 found out)

document.body.addEventListener('mouseout', function(e) {
    if (!e.relatedTarget && !e.toElement) {
        console.log('We\'re OUT !');
    }
});
Claudiu
  • 3,700
  • 1
  • 38
  • 35
  • 2
    Not working for me on chrome. `relatedTarget` is null when mouse leaves the window. Inspired by @user1084282's answer, change it to this: `if(!e.relatedTarget && !e.toElement) { ... }` and it works –  Apr 29 '17 at 17:28
  • I confirm that this works fine on Chrome, Firefox and IE Edge, if you use `if(!e.relatedTarget && !e.toElement)` instead of the condition in the actual answer. It detects the mouse leave the document body. – Haijerome Apr 30 '19 at 18:44
  • Do not have the event on document.body because the windows scrollbar may shrink it and fire abowe the scroll. And why do you have 'mouseout' that fires on all bubbled elements when you can have 'mouseleave'? https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseleave_event –  Jun 23 '19 at 01:14
4

I tried one after other and found a best answer at the time:

https://stackoverflow.com/a/3187524/985399

I skip old browsers so I made the code shorter to work on modern browsers (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>")

Here you see the browser support

That was pretty short I thought

But a problem still remained because "mouseout" trigg on all elements in the document.

To prevent it from happen, use mouseleave (IE5.5+). See the good explanation in the link.

The following code works without triggering on elements inside the element to be inside or outside of. Try also drag-release outside the document.

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>")

You can set the event on any HTML element. Do not have the event on document.body though, because the windows scrollbar may shrink the body and fire when mouse pointer is abowe the scroll bar when you want to scroll but not want to trigg a mouseLeave event over it. Set it on document instead, as in the example.

3

apply this css:

html
{
height:100%;
}

This ensures that the html element takes up the entire height of the window.

apply this jquery:

$("html").mouseleave(function(){
 alert('mouse out');
});

The problem with mouseover and mouseout is that if you mouse over/out of html to a child element it will set off the event. The function I gave isn't set off when mousing to a child element. It is only set off when you mouse out/in of the window

just for you to know you can do it for when the user mouses in the window:

$("html").mouseenter(function(){
  alert('mouse enter');
});
www139
  • 4,960
  • 3
  • 31
  • 56
  • 2
    By "I don't believe javascript has an equivalent function", you mean, you don't know that jQuery IS A javascript functions library and that jQuery IS written WITH and FOR javascript ? – Milche Patern Jul 09 '16 at 06:26
  • 2
    @MilchePatern I wrote this answer almost two years ago. I was more ignorant back then and I didn't realize the fact that anything done in jQuery can be done in pure JavaScript. I will update the answer... – www139 Jul 09 '16 at 14:56
3

See mouseover and mouseout.

var demo = document.getElementById('demo');
document.addEventListener("mouseout", function(e){demo.innerHTML="";});
document.addEventListener("mouseover", function(e){demo.innerHTML="";});
div { font-size:80vmin; position:absolute;
      left:50%; top:50%; transform:translate(-50%,-50%); }
<div id='demo'></div>
ashleedawg
  • 20,365
  • 9
  • 72
  • 105
3

I take back what i said. It is possible. I wrote this code, works perfectly.

window.onload = function() {

    $span = document.getElementById('text');

    window.onmouseout = function() {
        $span.innerHTML = "mouse out";  
    }

    window.onmousemove = function() {
        $span.innerHTML = "mouse in";   
    }

}

works in chrome, firefox, opera. Aint tested in IE but assume it works.

edit. IE as always causes trouble. To make it work in IE, replace the events from window to document:

window.onload = function() {

    $span = document.getElementById('text');

    document.onmousemove = function() {
        $span.innerHTML = "mouse move";
    }

    document.onmouseout = function() {
        $span.innerHTML = "mouse out";
    }

}

combine them for crossbrowser kick ass cursor detection o0 :P

Ozzy
  • 10,285
  • 26
  • 94
  • 138
  • Have you tested how well this works together with elements on the page? I would think that elements that have their own 'onmouseover/out' events could potentially cancel bubbling and break this functionality. – Dan Herbert May 28 '09 at 22:47
  • 1
    Ozzy, Dan is trying to explain that an element that stops event propagation will prevent the event from reaching the document/window, thereby rendering your solution ineffective. – James May 29 '09 at 00:22
  • I just tested it with other elements with their own onmousemove and onmouseout events and it still works fine. – Ozzy May 29 '09 at 02:19
  • 2
    Yes okay, but again we're talking about event propagation. If you purposefully stop event propagation with EVENT.stopPropagation() (for IE, EVENT.cancelBubble=true) then it won't bubble up to the document/window... To counteract this you could use event capturing in browsers that support it. – James May 29 '09 at 09:17
  • Remember that in JS to declare a variable, you must use 'var'. Now the variable $span is an implicit global, which is probably not what you want. Also in case it isn't intentional, you don't need a $ in variable names. – Jani Hartikainen Jul 06 '10 at 15:19
2

A combination of some of the answers here. And I included the code showing a model only once. And the model goes away when clicked anywhere else.

<script>
    var leave = 0
    //show modal when mouse off of page
    $("html").mouseleave(function() {
       //check for first time
       if (leave < 1) {
          modal.style.display = "block";
          leave = leave + 1;
       }
    });

    // Get the modal with id="id01"
       var modal = document.getElementById('id01');
            
    // When the user clicks anywhere outside of the modal, close it
       window.onclick = function(event) {
          if (event.target == modal) {
             modal.style.display = "none";
          }
       }
</script>
dippas
  • 58,591
  • 15
  • 114
  • 126
Morgan Hayes
  • 321
  • 2
  • 25
1

This might be a bit hacky but it will only trigger when the mouse leaves the window. It kept catching child events and this resolved it

 $(window).mouseleave(function(event) {
    if (event.toElement == null) {
    //Do something
    }
  })
dippas
  • 58,591
  • 15
  • 114
  • 126
0
$(window).mouseleave(function() {
 alert('mouse leave');
});
Sandeep Sherpur
  • 2,418
  • 25
  • 27
0

The following code should work with vanilla JS:

document.addEventListener("mouseleave", (event) => {  
  if (event.clientY <= 0 || event.clientX <= 0 || (event.clientX >= window.innerWidth || event.clientY >= window.innerHeight)) {  
    //your code  
  }  
});
MushroomFX
  • 23
  • 9
0

You could do an OnMouseOut function call on the body tag.

dippas
  • 58,591
  • 15
  • 114
  • 126
Ender
  • 14,995
  • 8
  • 36
  • 51
-2

This will work in chrome,

$(document).bind('dragleave', function (e) {
    if (e.originalEvent.clientX == 0){
        alert("left window");
    }
});
anupraj
  • 7
  • 2