Melhor maneira de detectar quando um usuário sai de uma página da web?

195

Qual é a melhor maneira de detectar se um usuário sai de uma página da web?

O onunloadevento JavaScript não funciona sempre (a solicitação HTTP leva mais tempo que o necessário para encerrar o navegador).

Criar um provavelmente será bloqueado pelos navegadores atuais.

ANaimi
fonte

Respostas:

219

Experimente o onbeforeunloadevento: ele é acionado imediatamente antes do descarregamento da página. Também permite que você pergunte novamente se o usuário realmente deseja sair. Veja a demonstração onbeforeunload Demo .

Como alternativa, você pode enviar uma solicitação do Ajax quando ele sair.

Andreas Petersson
fonte
3
Seria bom para o usuário se você rastreasse se o formulário mudasse e só solicitasse se o fizesse - para que não seja irritante.
adam0101
3
Observe também que vários navegadores móveis ignoram o resultado do evento (ou seja, eles não solicitam confirmação ao usuário). O Firefox tem uma preferência oculta em about: config para fazer o mesmo. Em essência, isso significa que o usuário sempre confirma que o documento pode ser descarregado.
MJB
2
Observe que esse método também evoca quando o usuário atualiza a página ou envia um formulário.
Zaytsev Dmitry
@ Andreas Petersson, tenho o mesmo desafio com o objetivo de abandonar a sessão do usuário. Como eu pude capturar o URL digitado para verificar se ele pertence ao projeto?
Jcc.Sanabria
21

A Mozilla Developer Network tem uma boa descrição e exemplo de onbeforeunload .

Se você deseja avisar o usuário antes de sair da página, se ela estiver suja (ou seja, se o usuário inseriu alguns dados):

window.addEventListener('beforeunload', function(e) {
  var myPageIsDirty = ...; //you implement this logic...
  if(myPageIsDirty) {
    //following two lines will cause the browser to ask the user if they
    //want to leave. The text of this dialog is controlled by the browser.
    e.preventDefault(); //per the standard
    e.returnValue = ''; //required for Chrome
  }
  //else: user is allowed to leave without a warning dialog
});
DuckMaestro
fonte
10

Aqui está uma solução alternativa: como na maioria dos navegadores, os controles de navegação (a barra de navegação, as guias etc.) estão localizados acima da área de conteúdo da página, você pode detectar o ponteiro do mouse saindo da página pela parte superior e exibir um " antes de sair " diálogo. É completamente discreto e permite que você interaja com o usuário antes que ele efetue a ação para sair.

$(document).bind("mouseleave", function(e) {
    if (e.pageY - $(window).scrollTop() <= 1) {    
        $('#BeforeYouLeaveDiv').show();
    }
});

A desvantagem é que, é claro, é um palpite de que o usuário realmente pretenda sair, mas na grande maioria dos casos está correto.

rustyx
fonte
Estou procurando algo parecido com, quero mostrar algo discreto ao meu usuário. Antes de sair da página, estou parecendo um evento suspenso no botão voltar, porque preciso disparar o evento antes que o usuário clique para voltar. Acredito que a melhor maneira é realmente detectar o documento de saída do corpo do mouse e fazer alguma lógica para determinar se o usuário interagiu com a página.
Leonardo Souza Paiva
8

Para isso eu estou usando:

window.onbeforeunload = function (e) {

}

É acionado imediatamente antes da página ser descarregada.

suhailvs
fonte
1
O Opera 12 e versões anteriores não suportam isso ... zachleat.com/web/…
Cyrus
Como isso é melhor do que as duas respostas anteriores que dão a mesma solução onbeforeunload?
Dan Dascalescu
5

Sei que esta pergunta foi respondida, mas caso você queira acionar algo apenas quando o NAVEGADOR real é fechado, e não apenas quando ocorre um carregamento de página, você pode usar este código:

window.onbeforeunload = function (e) {
        if ((window.event.clientY < 0)) {
            //window.localStorage.clear();
            //alert("Y coords: " + window.event.clientY)
        }
};

No meu exemplo, estou limpando o armazenamento local e alertando o usuário com as conexões do mouse e do mouse; somente quando o navegador estiver fechado, isso será ignorado em todas as cargas de página do programa.

Merr Leader
fonte
window.event é indefinido para mim
Karl Glaser
O exemplo do Merr Leader está errado, window.event deve ser usado apenas como fallback, por exemplo, para versões mais antigas do IE; em outros casos, o parâmetro de evento (a evariável nesse caso) deve ser usado: stackoverflow.com/questions/9813445/ ...
Sk8erPeter
Não chegaria ao ponto de dizer que estou errado. Meus exemplos funcionam bem para mim no IE e no Chrome. Meu exemplo foi o cenário em que o usuário clica no X (fechar) no navegador da web. Nem a maneira mais bonita, talvez, mas funciona.
Merr Leader
Do MDN: This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. developer.mozilla.org/pt-BR/docs/Web/API/Window/event
Robin Neal
3

Uma maneira (ligeiramente hacky) de fazer isso é substituir e os links que saem do seu site com uma chamada AJAX para o lado do servidor, indicando que o usuário está saindo, em seguida, use o mesmo bloco javascript para levar o usuário ao site externo solicitou.

É claro que isso não funcionará se o usuário simplesmente fechar a janela do navegador ou digitar um novo URL.

Para contornar isso, você poderia usar o setTimeout () do Javascript na página, fazendo uma chamada AJAX a cada poucos segundos (dependendo da rapidez com que você deseja saber se o usuário saiu).

Steve M
fonte
4
Se você usar a abordagem da página inicial do relatório, não será necessário modificar todos os links da página.
Vinko Vrsalovic 29/09/08
3

Graças ao Service Workers , é possível implementar uma solução semelhante à do Adam puramente no lado do cliente, desde que o navegador a suporte. Apenas evite solicitações de pulsação:

// The delay should be longer than the heartbeat by a significant enough amount that there won't be false positives
const liveTimeoutDelay = 10000
let liveTimeout = null

global.self.addEventListener('fetch', event => {
  clearTimeout(liveTimeout)
  liveTimeout = setTimeout(() => {
    console.log('User left page')
    // handle page leave
  }, liveTimeoutDelay)
  // Forward any events except for hearbeat events
  if (event.request.url.endsWith('/heartbeat')) {
    event.respondWith(
      new global.Response('Still here')
    )
  }
})
Jeffrey Sweeney
fonte
Você quis dizer que em algum lugar no código do cliente, precisa haver um setInterval(() => fetch('/heartbeat'), 5000)?
Dan Dascalescu
@DanDascalescu Correct. O código JS faz uma solicitação de rede para / heartbeat e o responsável pelo serviço a intercepta.
Jeffrey Sweeney
2

No caso de você precisar fazer algum código assíncrono (como enviar uma mensagem ao servidor de que o usuário não está focado na sua página no momento), o evento beforeunloadnão dará tempo para que o código assíncrono seja executado. No caso de assíncrono, descobri que os eventos visibilitychangee mouseleavesão as melhores opções. Esses eventos são acionados quando o usuário altera a guia, oculta o navegador ou remove o cursor do escopo da janela.

document.addEventListener('mouseleave', e=>{
     //do some async code
})

document.addEventListener('visibilitychange', e=>{
     if (document.visibilityState === 'visible') {
   //report that user is in focus
    } else {
     //report that user is out of focus
    }  
})

Tal Yaron
fonte
0

Quanto vale a pena, foi isso que fiz e talvez possa ajudar outras pessoas, mesmo que o artigo seja antigo.

PHP:

session_start();

$_SESSION['ipaddress'] = $_SERVER['REMOTE_ADDR'];

if(isset($_SESSION['userID'])){
    if(!strpos($_SESSION['activeID'], '-')){
        $_SESSION['activeID'] = $_SESSION['userID'].'-'.$_SESSION['activeID'];
    }
}elseif(!isset($_SESSION['activeID'])){
    $_SESSION['activeID'] = time();
}

JS

window.setInterval(function(){
            var userid = '<?php echo $_SESSION['activeID']; ?>';
            var ipaddress = '<?php echo $_SESSION['ipaddress']; ?>';
            var action = 'data';

            $.ajax({
                url:'activeUser.php',
                method:'POST',
                data:{action:action,userid:userid,ipaddress:ipaddress},
                success:function(response){
                     //alert(response);                 
                }
            });
          }, 5000);

Chamada Ajax para activeUser.php

if(isset($_POST['action'])){
    if(isset($_POST['userid'])){
        $stamp = time();
        $activeid = $_POST['userid'];
        $ip = $_POST['ipaddress'];

        $query = "SELECT stamp FROM activeusers WHERE activeid = '".$activeid."' LIMIT 1";
        $results = RUNSIMPLEDB($query);

        if($results->num_rows > 0){
            $query = "UPDATE activeusers SET stamp = '$stamp' WHERE activeid = '".$activeid."' AND ip = '$ip' LIMIT 1";
            RUNSIMPLEDB($query);
        }else{
            $query = "INSERT INTO activeusers (activeid,stamp,ip)
                    VALUES ('".$activeid."','$stamp','$ip')";
            RUNSIMPLEDB($query);
        }
    }
}

Base de dados:

CREATE TABLE `activeusers` (
  `id` int(11) NOT NULL,
  `activeid` varchar(20) NOT NULL,
  `stamp` int(11) NOT NULL,
  `ip` text
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Basicamente, a cada 5 segundos, os js postam em um arquivo php que rastreia o usuário e o endereço IP dos usuários. Usuários ativos são simplesmente um registro de banco de dados que possui uma atualização para o registro de data e hora do banco de dados em 5 segundos. Usuários antigos param de atualizar para o banco de dados. O endereço IP é usado apenas para garantir que um usuário seja único, para que 2 pessoas no site ao mesmo tempo não se registrem como 1 usuário.

Provavelmente não é a solução mais eficiente, mas faz o trabalho.

Adão
fonte