¿La mejor manera de detectar cuándo un usuario abandona una página web?

195

¿Cuál es la mejor manera de detectar si un usuario abandona una página web?

El onunloadevento JavaScript no funciona cada vez (la solicitud HTTP tarda más que el tiempo requerido para terminar el navegador).

La creación de uno probablemente será bloqueada por los navegadores actuales.

ANaimi
fuente

Respuestas:

219

Pruebe el onbeforeunloadevento: se dispara justo antes de que se descargue la página. También le permite volver a preguntar si el usuario realmente quiere irse. Vea la demostración onbeforeunload Demo .

Alternativamente, puede enviar una solicitud de Ajax cuando se vaya.

Andreas Petersson
fuente
3
Sería bueno para el usuario si realiza un seguimiento si el formulario cambia y solo solicita si lo hizo, por lo que no es molesto.
adam0101
3
Tenga en cuenta también que varios navegadores móviles ignoran el resultado del evento (es decir, no le piden confirmación al usuario). Firefox tiene una preferencia oculta en about: config para hacer lo mismo. En esencia, esto significa que el usuario siempre confirma que el documento puede estar descargado.
MJB
2
Tenga en cuenta que este método también evoca cuando el usuario actualiza la página o envía un formulario.
Zaytsev Dmitry
@ Andreas Petersson, tengo el mismo desafío con el objetivo de abandonar la sesión del usuario. ¿Cómo puedo capturar la url escrita para verificar si pertenece al proyecto?
Jcc.Sanabria
21

Mozilla Developer Network tiene una buena descripción y un ejemplo de onbeforeunload .

Si desea advertir al usuario antes de abandonar la página si su página está sucia (es decir, si el usuario ha ingresado algunos datos):

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
});
PatoMaestro
fuente
10

Aquí hay una solución alternativa: dado que en la mayoría de los navegadores los controles de navegación (la barra de navegación, las pestañas, etc.) se encuentran sobre el área de contenido de la página, puede detectar el puntero del mouse que sale de la página por la parte superior y mostrar un " antes de salir " diálogo. Es completamente discreto y le permite interactuar con el usuario antes de que realmente realice la acción de irse.

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

La desventaja es que, por supuesto, es una suposición que el usuario realmente tiene la intención de irse, pero en la gran mayoría de los casos es correcto.

rustyx
fuente
Estoy buscando algo así, quiero mostrar algo discreto a mi usuario, antes de salir de mi página, estoy buscando algo así como un evento flotante en el botón Atrás, porque necesito activar el evento antes de que el usuario haga clic para volver. Creo que la mejor manera es realmente detectar el documento del cuerpo del mouse y hacer algo de lógica para determinar si el usuario interactuó con la página.
Leonardo Souza Paiva el
8

Para esto estoy usando:

window.onbeforeunload = function (e) {

}

Se dispara justo antes de que se descargue la página.

suhailvs
fuente
1
Opera 12 y versiones anteriores no son compatibles con esto ... zachleat.com/web/…
Cyrus
¿Cómo es esto mejor que las dos respuestas anteriores que dan la misma solución onbeforeunload?
Dan Dascalescu
5

Sé que esta pregunta ha sido respondida, pero en caso de que solo desee que algo se active cuando se cierra el NAVEGADOR real, y no solo cuando se produce una carga de página, puede usar este código:

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

En mi ejemplo, estoy limpiando el almacenamiento local y alertando al usuario con los ratones y los cables, solo cuando el navegador está cerrado, esto se ignorará en todas las cargas de página desde el programa.

Líder Merr
fuente
window.event no está definido para mí
Karl Glaser
El ejemplo de Merr Leader es incorrecto, window.event solo debe usarse como una alternativa, por ejemplo, para versiones anteriores de IE, en otros casos, el parámetro de evento (la evariable en este caso) debe usarse: stackoverflow.com/questions/9813445/ ...
Sk8erPeter
No iría tan lejos como para decir que estoy equivocado. Mis ejemplos me funcionan bien en IE y Chrome. Mi ejemplo fue para el escenario en el que el usuario hace clic en la X (cerrar) en el navegador web. Tampoco la forma más bonita, pero funciona.
Merr Leader
Desde 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/en-US/docs/Web/API/Window/event
Robin Neal
3

Una forma (ligeramente hacky) de hacerlo es reemplazar y enlaces que se alejan de su sitio con una llamada AJAX al lado del servidor, lo que indica que el usuario se va, luego use ese mismo bloque de JavaScript para llevar al usuario al sitio externo He solicitado.

Por supuesto, esto no funcionará si el usuario simplemente cierra la ventana del navegador o escribe una nueva URL.

Para evitar eso, es posible que necesite usar setTimeout () de Javascript en la página, haciendo una llamada AJAX cada pocos segundos (dependiendo de qué tan rápido quiera saber si el usuario se ha ido).

Steve M
fuente
44
Si utiliza el enfoque de inicio del informe, no es necesario modificar todos los enlaces de la página.
Vinko Vrsalovic
3

Gracias a Service Workers , es posible implementar una solución similar a la de Adam únicamente en el lado del cliente, siempre que el navegador lo admita. Simplemente evite las solicitudes de latidos:

// 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
fuente
¿Quiso decir que en algún lugar del código del cliente, debe haber un setInterval(() => fetch('/heartbeat'), 5000)?
Dan Dascalescu
@DanDascalescu Correcto. El código JS realiza una solicitud de red a / heartbeat, y el trabajador del servicio la intercepta.
Jeffrey Sweeney
2

En el caso de que necesite hacer un código asincrónico (como enviar un mensaje al servidor de que el usuario no está enfocado en su página en este momento), el evento beforeunloadno dará tiempo para que se ejecute el código asíncrono. En el caso de async, descubrí que los eventos visibilitychangey mouseleaveson las mejores opciones. Estos eventos se disparan cuando el usuario cambia de pestaña, oculta el navegador o saca al curso del alcance de la ventana.

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
fuente
0

Por lo que vale, esto es lo que hice y tal vez pueda ayudar a otros a pesar de que el artículo es antiguo.

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

Llamada Ajax a 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 datos:

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;

Básicamente, cada 5 segundos, el js publicará en un archivo php que rastreará al usuario y la dirección IP del usuario. Los usuarios activos son simplemente un registro de base de datos que tiene una actualización de la marca de tiempo de la base de datos en 5 segundos. Los viejos usuarios dejan de actualizar a la base de datos. La dirección IP se usa solo para garantizar que un usuario sea único, de modo que 2 personas en el sitio al mismo tiempo no se registren como 1 usuario.

Probablemente no sea la solución más eficiente, pero hace el trabajo.

Adán
fuente