El evento al salir de la página en JavaScript (beforeunload)
Controlar el momento en que un usuario decide abandonar tu sitio web es fundamental para evitar la pérdida de datos, como cuando un usuario cierra la pestaña a mitad de rellenar un formulario largo. En este tutorial, exploraremos cómo funciona el evento beforeunload, sus severas (pero necesarias) restricciones modernas y cómo implementarlo correctamente.
1. El ciclo de vida de una página HTML
Para entender cuándo y cómo actuar, primero debemos ubicar este momento dentro del ciclo de vida de una página web. El navegador pasa por tres estados clave representados por eventos:
DOMContentLoaded: El HTML se ha cargado por completo y el árbol DOM está listo, pero recursos externos como imágenes u hojas de estilo aún pueden estar pendientes.load: La página y todos sus recursos (imágenes, CSS, etc.) se han cargado por completo.beforeunload/unload: El usuario está a punto de abandonar o ya ha abandonado la página.
2. Detectar cuando el usuario se va: beforeunload
El evento beforeunload se dispara en el objeto window justo antes de que los recursos de la página comiencen a descargarse debido a que el usuario cierra la pestaña, recarga la web o navega hacia otro enlace.
Ejemplo 1: El método moderno y compatible
Para activar la ventana de confirmación del navegador, debemos interactuar con el evento de una forma muy específica. Aunque parezca intuitivo usar event.preventDefault(), la gran mayoría de los navegadores modernos ignoran esta instrucción en este evento en particular.
Para que funcione de forma consistente en todos los navegadores, debemos asignar un valor a event.returnValue y, por compatibilidad con navegadores antiguos, retornar una cadena de texto:
JavaScript
JavaScript
window.addEventListener("beforeunload", (event) => {
// 1. Cancelamos el evento de forma estándar asignando una cadena a returnValue
event.returnValue = "Hay cambios sin guardar.";
// 2. Por compatibilidad histórica con navegadores antiguos, también retornamos el texto
return "Hay cambios sin guardar.";
});
3. ¡Cuidado! Las limitaciones de personalización
En el pasado, los desarrolladores abusaban de este evento mostrando mensajes engañosos o publicidad molesta cuando el usuario intentaba salir. Por este motivo, los navegadores modernos han capado casi por completo su personalización:
- No puedes cambiar el texto: Da igual la frase que escribas en el
returno enreturnValue, el navegador la ignorará por completo. En su lugar, mostrará un mensaje genérico del sistema (por ejemplo: «¿Quieres salir de este sitio web? Es posible que los cambios no se guarden»). - No puedes personalizar los botones: Los botones de «Salir» y «Permanecer» los renderiza el sistema operativo o el navegador y no se pueden modificar con CSS ni JS.
- No puedes saber directamente qué eligió el usuario: No hay un evento nativo que te diga si el usuario hizo clic en «Permanecer».
El truco del temporizador para saber si se quedó:
Aunque no hay un callback directo para el botón «Permanecer», puedes usar un truco asíncrono. Al dispararse
beforeunload, inicias un temporizador (setTimeout). Si el usuario decide irse, la página se destruirá y el temporizador nunca se ejecutará. Si decide quedarse, el temporizador se ejecutará tras unos segundos en segundo plano, permitiéndote saber indirectamente que el usuario canceló la salida:JavaScript
window.addEventListener("beforeunload", () => { setTimeout(() => { console.log("El usuario decidió quedarse en la página"); // Aquí puedes ejecutar acciones reactivas }, 1000); event.returnValue = ""; });
4. Resumen de buenas prácticas
- Solo actívalo cuando sea necesario: No mantengas este evento activo todo el tiempo; resulta molesto si el usuario solo está navegando de forma normal. Actívalo únicamente cuando detectes que un formulario ha sido modificado (
dirty) y bórralo cuando el formulario sea enviado con éxito. - Evita llamadas asíncronas pesadas: No intentes hacer peticiones de red normales (
fetchoaxios) pesadas dentro debeforeunloadounload, ya que el navegador podría cerrarse antes de completarse. Para enviar estadísticas de salida de forma segura, utilizanavigator.sendBeacon().
5. Resumen del Ciclo de Vida y Eventos
| Evento | ¿Cuándo se activa? | ¿Se puede cancelar/frenar? | Uso recomendado |
DOMContentLoaded | DOM listo (sin imágenes/CSS aún). | No | Inicializar scripts, buscar nodos y configurar la interfaz. |
load | Página y todos sus recursos cargados. | No | Calcular tamaños reales de imágenes, aplicar estilos dinámicos. |
beforeunload | El usuario inicia la salida. | Sí (vía returnValue) | Confirmar si hay cambios sin guardar antes de perderlos. |
unload | El usuario ya se está yendo definitivamente. | No | Enviar analíticas o estadísticas de uso rápidas mediante sendBeacon. |
Conclusión
El evento beforeunload es tu última línea de defensa para proteger la experiencia de usuario frente a pérdidas accidentales de datos. Aunque los navegadores modernos han restringido la personalización visual de este evento por motivos de seguridad y experiencia de usuario, dominar la sintaxis correcta de returnValue y entender sus limitaciones te ahorrará comportamientos inesperados en producción.