Como posso detectar quando o mouse sai da janela?

102

Eu quero ser capaz de detectar quando o mouse sai da janela para que eu possa impedir que eventos sejam disparados enquanto o mouse do usuário está em outro lugar.

Alguma ideia de como fazer isso?


fonte
1
Confira esta biblioteca do github com intenção de saída. É ótimo. github.com/beeker1121/exit-intent-popup
raksheetbhat
Use mouseleave para evitar o disparo em todos os elementos como o mouseout faz: Explicação e suporte muito bons: developer.mozilla.org/en-US/docs/Web/API/Element/…
Defina o evento no documento - não no document.body para evitar o disparo por meio da barra de rolagem. A barra de rolagem do Windows parece encolher o corpo.

Respostas:

100

Lembre-se de que minha resposta envelheceu muito.

Este tipo de comportamento é geralmente desejado durante a implementação do comportamento arrastar e soltar em uma página html. A solução abaixo foi testada no IE 8.0.6, FireFox 3.6.6, Opera 10.53 e Safari 4 em uma máquina MS Windows XP.
Primeiro, uma pequena função de Peter-Paul Koch; manipulador de eventos para vários navegadores:

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

Em seguida, use este método para anexar um manipulador de eventos ao evento mouseout dos objetos de documento:

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

Por fim, aqui está uma página html com o script incorporado para depuração:

<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
fonte
Usar window.event corrigiu meu problema ao obter este evento. Obrigado!
Jay,
parece que isso não dispara quando o mouse não é pressionado no cromo. ele dispara quando o mouse é pressionado em cromo. parece um comportamento inconsistente.
antony.trupe
5
Isso não funciona se outros elementos HTML estiverem preenchendo a janela.
Emmanuel
O problema com essa solução é que ela alerta a janela esquerda mesmo se o usuário apenas tentar usar o scroller. Publiquei a solução para este problema aqui , mas ainda há um problema que precisa ser resolvido (veja o link).
JohnyFree,
1
Use a tecla mouseeleave em vez de mouseout para evitar o disparo em elementos do documento. Explicação muito boa e funciona em IE5.5 ... developer.mozilla.org/en-US/docs/Web/API/Element/…
41

Se você está usando jQuery, que tal este código curto e doce -

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

Este evento será acionado sempre que o mouse não estiver na sua página como você deseja. Basta alterar a função para fazer o que quiser.

E você também pode usar:

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

Para disparar quando o mouse entrar de volta na página novamente.

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

Mandeep Janjua
fonte
mouseleave está funcionando, mas também dispara se o elemento de inspeção for aberto, como prevenir? Idéia?
Pankaj Verma
Esta técnica não é estável no Chrome 15 a 56 (minha versão atual). Mas funciona muito bem no Firefox. Consulte: stackoverflow.com/questions/7448468/…
Tremours,
mouseleave parece disparar muito mais do que deveria.
Alexander
Ele também dispara se a janela estiver em segundo plano (navegador sem foco) e o mouse entrar na janela (Chrome 61 / macOS10.11)
CodeBrauer
1
@Skeets (e futuros leitores) Este problema foi marcado como corrigido em 14 de fevereiro de 2019
Xandor
27

Isso funciona para mim:

addEvent(document, 'mouseout', function(evt) {
  if (evt.toElement == null && evt.relatedTarget == null) {
    alert("left window");
  }
});
user1084282
fonte
9
O que é addEvent?
Yi Jiang
5
é uma função definida na resposta de Joshua Mills
superjos
4
em ie8, relatedTarget é indefinido, em ie9 + e firefox, toElement é indefinido. Por isso, a avaliação correta é: if ((evt.relatedTarget === null) || (evt.toElement === null)) {
carlos
1
O predicado poderia ser mais conciso como emif (!evt.toElement && !evt.relatedTarget)
Pavel Ye
20

Para detectar a saída do mouse sem levar em conta a barra de rolagem e o campo de preenchimento automático ou inspecionar:

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

  }
});

Explicações das condições:

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

============================ EDITAR =========================== ======

document.addEventListener ("mouseleave") parece não ser acionado na nova versão do firefox, mouseleave precisa ser anexado a um elemento como o corpo ou um elemento filho.

Eu sugiro usar ao invés

document.body.addEventListener("mouseleave")

Ou

window.addEventListener("mouseout")
Daphoque
fonte
de vez em quando não disparou. Parece que falha quando um ponto de fluxo de pontos é acionado pelo cursor em movimento na borda e o cursor torna-se redimensionado.
Ivan Borshchov
@ user3479125, você poderia experimentar em meu snippet também e ver se o problema permanece quando a instrução if não é adicionada? Ele leva em conta a barra de rolagem, mas não testei com preenchimento automático ou inspeção. stackoverflow.com/a/56678357/985399
7

Usar o evento onMouseLeave evita bolhas e permite detectar facilmente quando o mouse sai da janela do navegador.

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

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

Samy Bencherif
fonte
Fantástico! O suporte é IE5.5 + ... Funciona também: document.onmouseleave = function() { alert('You left!') };não use document.body que dispara uma barra de rolagem que encolhe o corpo! Código para prevenir fogo em elementos não é necessário. Boa explicação: developer.mozilla.org/en-US/docs/Web/API/Element/…
6

Nenhuma dessas respostas funcionou para mim. Agora estou usando:

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

});

Estou usando isso para arrastar e soltar o widget de upload de arquivos. Não é absolutamente preciso, sendo acionado quando o mouse chega a uma certa distância da borda da janela.

Emmanuel
fonte
Acredito que isso às vezes não será acionado se o movimento do mouse for "rápido o suficiente".
John Weisz de
@JohnWeisz Sim, acho que você está certo. Eu usei esse script por um tempo e ele funciona bem.
Emmanuel
6

Tentei todas as opções acima, mas nada parece funcionar conforme o esperado. Depois de um pouco de pesquisa descobri que e.relatedTarget é o html apenas antes de o rato sai da janela .

Então ... eu acabei com isso:


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

Informe-nos se encontrar algum problema ou melhoria!

Atualização de 2019

(como o usuário 1084282 descobriu)

document.body.addEventListener('mouseout', function(e) {
    if (!e.relatedTarget && !e.toElement) {
        console.log('We\'re OUT !');
    }
});
Claudiu
fonte
2
Não está funcionando para mim no Chrome. relatedTargeté nulo quando o mouse sai da janela. Inspirado na resposta de @ user1084282, mude para isto: if(!e.relatedTarget && !e.toElement) { ... }e funciona
Confirmo que isso funciona bem no Chrome, Firefox e IE Edge, se você usar em if(!e.relatedTarget && !e.toElement)vez da condição na resposta real. Ele detecta que o mouse saiu do corpo do documento.
Haijerome
Não tenha o evento em document.body porque a barra de rolagem do Windows pode encolhê-lo e disparar sobre a rolagem. E por que você tem 'mouseout' que dispara em todos os elementos borbulhantes quando você pode ter 'mouseleave'? developer.mozilla.org/en-US/docs/Web/API/Element/…
2

Retiro o que disse. É possível. Eu escrevi esse código, funciona perfeitamente.

window.onload = function() {

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

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

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

}

trabalha em cromo, firefox, ópera. Aint testado no IE, mas presume que funciona.

editar. O IE, como sempre, causa problemas. Para fazer funcionar no IE, substitua os eventos de janela para documento:

window.onload = function() {

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

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

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

}

combine-os para a detecção do cursor crossbrowser kick ass o0: P

Ozzy
fonte
Você testou como isso funciona bem com os elementos da página? Eu acho que os elementos que têm seus próprios eventos 'onmouseover / out' podem cancelar o borbulhamento e interromper essa funcionalidade.
Dan Herbert
1
Ozzy, Dan está tentando explicar que um elemento que interrompe a propagação do evento impedirá que o evento chegue ao documento / janela, tornando sua solução ineficaz.
James
Eu apenas testei com outros elementos com seus próprios eventos onmousemove e onmouseout e ainda funciona bem.
Ozzy
2
Sim, tudo bem, mas novamente estamos falando sobre propagação de eventos. Se você interromper propositalmente a propagação de eventos com EVENT.stopPropagation () (para IE, EVENT.cancelBubble = true), ela não aparecerá no documento / janela ... Para neutralizar isso, você pode usar a captura de eventos em navegadores que a suportam .
James
Lembre-se de que em JS para declarar uma variável, você deve usar 'var'. Agora, a variável $ span é um global implícito, o que provavelmente não é o que você deseja. Além disso, caso não seja intencional, você não precisa de um $ nos nomes das variáveis.
Jani Hartikainen
2

aplique este css:

html
{
height:100%;
}

Isso garante que o elemento html ocupe toda a altura da janela.

aplique este jquery:

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

O problema com o mouseover e o mouseout é que, se você passar o mouse sobre / fora do html para um elemento filho, o evento será ativado. A função que dei não é acionada ao passar o mouse para um elemento filho. Só é ativado quando você move o mouse para fora / dentro da janela

apenas para você saber que pode fazer isso quando o usuário passar o mouse na janela:

$("html").mouseenter(function(){
  alert('mouse enter');
});
www139
fonte
2
Por "Não acredito que o javascript tenha uma função equivalente", você quer dizer que não sabe que o jQuery É uma biblioteca de funções do javascript e que o jQuery É escrito COM e PARA o javascript?
Milche Patern
2
@MilchePatern Escrevi esta resposta há quase dois anos. Eu era mais ignorante naquela época e não percebia o fato de que qualquer coisa feita em jQuery pode ser feita em JavaScript puro. Vou atualizar a resposta ...
www139
2

Tentei um após o outro e encontrei a melhor resposta na época:

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

Eu pulo os navegadores antigos, então tornei o código mais curto para funcionar em navegadores modernos (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>")

Aqui você vê o suporte do navegador

Isso foi bem curto eu pensei

Mas ainda havia um problema porque o "mouseout" disparava em todos os elementos do documento .

Para evitar que isso aconteça, use o mouseeleave (IE5.5 +). Veja a boa explicação no link.

O código a seguir funciona sem acionar elementos dentro do elemento para estar dentro ou fora dele. Tente também soltar e arrastar para fora do documento.

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

Você pode definir o evento em qualquer elemento HTML. Não document.bodyative o evento , porque a barra de rolagem do Windows pode encolher o corpo e disparar quando o ponteiro do mouse está sobre a barra de rolagem quando você deseja rolar, mas não deseja acionar um evento mouseLeave sobre ele. Em documentvez disso, ative-o, como no exemplo.


fonte
1

Talvez se você estiver ouvindo OnMouseOver constantemente na tag do corpo, então callback quando o evento não estiver ocorrendo, mas, como Zack afirma, isso pode ser muito feio, porque nem todos os navegadores lidam com eventos da mesma maneira, há até alguns possibilidade de você perder o MouseOver mesmo por estar sobre um div na mesma página.

Pablo Albornoz
fonte
Não é uma resposta
1

Talvez isso ajudasse alguns dos que viriam aqui mais tarde. window.onblure document.mouseout.

window.onblur é acionado quando:

  • Você muda para outra janela usando Ctrl+Tabou Cmd+Tab.
  • Você se concentra (não apenas com o mouse) no inspetor de documentos.
  • Você troca de desktops.

Basicamente, a qualquer momento que a guia do navegador perde o foco.

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

document.mouseout é acionado quando:

  • Você move o cursor para a barra de título.
  • Você muda para outra janela usando Ctrl+Tabou Cmd+Tab.
  • Você abre e move o cursor para o inspetor de documentos.

Basicamente, em qualquer caso, quando o cursor sai do documento.

document.onmouseleave = function(){
    console.log("Mouse Out!")
}
Jayant Bhawal
fonte
"Asically sempre que ESSA guia do navegador perde o foco." .. com versões mais antigas do IE, o evento 'blur' é compilado por DOM quando / após outro elemento receber o foco, para gooChrome, é quando o próprio elemento perde o foco .. só para mencionar.
Milche Patern
Uso inteligente window.onblurtambém. +1
Redwolf Programs
1
$(window).mouseleave(function(event) {
  if (event.toElement == null) {
    //Do something
  }
})

Isso pode ser um pouco hackeado, mas só disparará quando o mouse sair da janela. Continuei capturando eventos infantis e isso resolveu

Cekaleek
fonte
1
$(window).mouseleave(function() {
 alert('mouse leave');
});
Sandeep Kumar
fonte
1

Isso funcionou para mim. Uma combinação de algumas das respostas aqui. E eu incluí o código mostrando um modelo apenas uma vez. E o modelo desaparece quando clicado em qualquer outro lugar.

<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>
Morgan Hayes
fonte
Não havia nenhuma tag jQuery na pergunta
mrReiha
jQuery é muito código em comparação com a minha resposta que funciona em navegadores modernos IE9 + e o resto. Baseia-se nesta resposta, mas muito mais curta: stackoverflow.com/a/3187524/985399
1

Veja mouseovere 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
fonte
0

Eu não testei isso, mas meu instinto seria fazer uma chamada de função OnMouseOut na tag body.

Ender
fonte
-2

Isso funcionará no Chrome,

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