¿Cómo puedo anular el cuadro de diálogo OnBeforeUnload y reemplazarlo por el mío?

346

Necesito advertir a los usuarios sobre los cambios no guardados antes de que abandonen una página (un problema bastante común).

window.onbeforeunload=handler

Esto funciona pero genera un diálogo predeterminado con un mensaje estándar irritante que envuelve mi propio texto. Necesito reemplazar completamente el mensaje estándar, por lo que mi texto es claro, o (aún mejor) reemplazar todo el diálogo con un diálogo modal usando jQuery.

Hasta ahora he fallado y no he encontrado a nadie más que parezca tener una respuesta. ¿Es posible?

Javascript en mi página:

<script type="text/javascript">   
   window.onbeforeunload=closeIt;
</script>

La función closeIt ():

function closeIt()
{
  if (changes == "true" || files == "true")
  {
      return "Here you can append a custom message to the default dialog.";
  }
}

Usando jQuery y jqModal, he intentado este tipo de cosas (usando un diálogo de confirmación personalizado):

$(window).beforeunload(function() {
        confirm('new message: ' + this.href + ' !', this.href);
        return false;
    });

que tampoco funciona: parece que no puedo vincularme al evento beforeunload.

carne
fuente
1
¿Puedes dar un ejemplo de tu código actual y de lo que produce?
Owen
Owen está en lo correcto. Y, la razón detrás de esto es la seguridad. Evitar que una página se descargue es útil en formularios web y demás, pero un sitio malicioso puede explotarlo fácilmente para engañar al usuario para que permanezca en una página. Es por eso que los navegadores web implementan el mensaje estándar y tienen este mecanismo para insertar texto personalizado.
pluckyglen
ver aquí para más información: stackoverflow.com/questions/140460
Ben McIntyre
1
no es posible en Chrome, referencia: stackoverflow.com/questions/37782104/…
Abhijeet

Respuestas:

299

No puede modificar el diálogo predeterminado para onbeforeunload, por lo que su mejor opción puede ser trabajar con él.

window.onbeforeunload = function() {
    return 'You have unsaved changes!';
}

Aquí hay una referencia a esto de Microsoft:

Cuando se asigna una cadena a la propiedad returnValue de window.event, aparece un cuadro de diálogo que ofrece a los usuarios la opción de permanecer en la página actual y conservar la cadena que se le asignó. La declaración predeterminada que aparece en el cuadro de diálogo, "¿Está seguro de que desea navegar fuera de esta página? ... Presione OK para continuar, o Cancelar para permanecer en la página actual", no se puede eliminar ni modificar.

El problema parece ser:

  1. Cuando onbeforeunloadse llama, tomará el valor de retorno del controlador como window.event.returnValue.
  2. Luego analizará el valor de retorno como una cadena (a menos que sea nulo).
  3. Como falsese analiza como una cadena, se activará el cuadro de diálogo, que luego pasará un true/ false.

El resultado es que no parece ser una manera de asignar falsea onbeforeunloadpara evitar que el diálogo por defecto.

Notas adicionales sobre jQuery:

  • Establecer el evento en jQuery puede ser problemático, ya que eso también permite onbeforeunloadque ocurran otros eventos. Si solo desea que su evento de descarga ocurra, me quedaré con JavaScript.
  • jQuery no tiene un acceso directo para, onbeforeunloadpor lo que tendría que usar la bindsintaxis genérica .

    $(window).bind('beforeunload', function() {} );

Edición 09/04/2018 : los mensajes personalizados en los cuadros de diálogo onbeforeunload están en desuso desde chrome-51 (cf: nota de la versión )

Owen
fuente
20
He encontrado que el ejemplo de JQuery de dado al final no funciona en Firefox. Siga con la forma simple en su lugar: window.onbeforeunload = function () {...}
jb.
21
Solo una nota de que Firefox se cambió recientemente para que el texto en el cuadro de diálogo ni siquiera se pueda personalizar: bugzilla.mozilla.org/show_bug.cgi?id=588292
David Hammond
15
Pero, ¿cómo proporciona Facebook un cuadro de diálogo personalizado? por ejemplo, vaya a la página de mensajes, responda el mensaje de su amigo y luego, antes de hacer clic en enviar, intente hacer clic en otro enlace. verá un cuadro de diálogo emergente personalizado
Varun
20
@Varun: noticias antiguas, pero Facebook posiblemente está poniendo controladores en el evento de clic de todos sus anclajes, pero si reescribe la URL en el navegador o cierra la ventana, vuelve al método estándar sobre el cual no tienen control.
Tabloo Quijico
1
@Varun También estoy sorprendido de que esta respuesta, que es imposible, se marque como la respuesta y tenga 150 votos positivos ... Acabo de darme cuenta, el mensaje personalizado de FB solo está disponible a través de enlaces dentro de su sitio. No si simplemente navegas lejos ...
Sébastien Richer
28

Lo que funcionó para mí, usando jQuery y probado en IE8, Chrome y Firefox, es:

$(window).bind("beforeunload",function(event) {
    if(hasChanged) return "You have unsaved changes";
});

Es importante no devolver nada si no se requiere un aviso, ya que aquí hay diferencias entre IE y otros comportamientos del navegador.

zampullín
fuente
Interesante, event.returnValuees el único método "aprobado" en IE. IE continúa diciendo "El valor de esta propiedad tiene prioridad sobre los valores devueltos por la función, como a través de una declaración de retorno de Microsoft JScript". .. sería bueno ver una correlación garantizada entre return(del evento) y event.returnValue..
21

Si bien no hay nada que pueda hacer sobre el cuadro en algunas circunstancias, puede interceptar a alguien haciendo clic en un enlace. Para mí, valió la pena el esfuerzo para la mayoría de los escenarios y, como alternativa, dejé el evento de descarga.

He usado Boxy en lugar del jQuery Dialog estándar, está disponible aquí: http://onehackoranother.com/projects/jquery/boxy/

$(':input').change(function() {
    if(!is_dirty){
        // When the user changes a field on this page, set our is_dirty flag.
        is_dirty = true;
    }
});

$('a').mousedown(function(e) {
    if(is_dirty) {
        // if the user navigates away from this page via an anchor link, 
        //    popup a new boxy confirmation.
        answer = Boxy.confirm("You have made some changes which you might want to save.");
    }
});

window.onbeforeunload = function() {
if((is_dirty)&&(!answer)){
            // call this if the box wasn't shown.
    return 'You have made some changes which you might want to save.';
    }
};

Puede adjuntar a otro evento y filtrar más sobre qué tipo de ancla se hizo clic, pero esto funciona para mí y lo que quiero hacer y sirve como un ejemplo para que otros lo usen o mejoren. Pensé en compartir esto para aquellos que desean esta solución.

He recortado el código, por lo que esto puede no funcionar como está.

Dan Power
fuente
return 'Ha realizado algunos cambios que quizás desee guardar'. no funciono para mi Tuve que almacenar el mensaje en una variable que diga 'warning_message' y devolví warning_message. ¡Entonces solo funcionó para mí!
Suganya
¿Probaste en IE, Chrome, Firefox?
Kiquenet
9

1) Use onbeforeunload, no onunload.

2) Lo importante es evitar ejecutar una declaración de devolución. No me refiero, con esto, a evitar regresar de su controlador. Devuelve todo bien, pero lo hace asegurándose de llegar al final de la función y NO ejecuta una declaración de devolución. En estas condiciones, el cuadro de diálogo estándar incorporado no se produce.

3) Puede, si usa onbeforeunload, ejecutar una llamada ajax en su controlador unbeforeunload para ordenar el servidor, pero debe ser síncrono, y debe esperar y manejar la respuesta en su controlador onbeforeunload (aún respetando condición (2) arriba). Hago esto y funciona bien. Si realiza una llamada ajax síncrona, todo se retiene hasta que la respuesta regrese. Si hace una asincrónica, pensando que no le importa la respuesta del servidor, la descarga de la página continúa y este proceso cancela su llamada ajax, incluido un script remoto si se está ejecutando.

usuario1164763
fuente
Lo digo cualquiera que lea esto ^ Chrome está deprecating llamadas AJAX síncronos en unbeforeunload
morganwebdev
4

Intente colocar un return;mensaje en lugar de un mensaje ... esto funciona para la mayoría de los navegadores. (Esto solo evita realmente los regalos de diálogo)

window.onbeforeunload = function(evt) {            
        //Your Extra Code
        return;
}
Donald Powell
fuente
3
return null evitará que se abra el diálogo
Brett Weber
1
return null NO impedirá que se abra el diálogo. Lo probé en Chrome y no es el caso.
Ray
2
Puedo confirmar el comentario de Ray en IE también. Al volver, nullse abrirá un cuadro de diálogo con el texto "nulo". Sin embargo, return void(0);no abrirá el cuadro de diálogo.
MCattle
1
Al no poner ninguna declaración de retorno dentro de la función beforeunload, el diálogo no se abrirá.
OuuGiii
3

Puede detectar qué botón (ok o cancelar) presionado por el usuario, porque la función de descarga solo se llama cuando el usuario decide salir de la página. Aunque en esta función las posibilidades son limitadas, porque el DOM se está colapsando. Puede ejecutar javascript, pero ajax POST no hace nada, por lo tanto, no puede usar este método para cerrar sesión automáticamente. Pero hay una solución para eso. El window.open ('logout.php') ejecutado en la función de descarga, por lo que el usuario cerrará sesión con una nueva ventana abierta.

function onunload = (){
    window.open('logout.php');
}

Este código se llama cuando el usuario abandona la página o cierra la ventana activa y el usuario cierra sesión con 'logout.php'. La nueva ventana se cierra inmediatamente cuando el cierre de sesión de php consta de código:

window.close();
Tamas Cseh
fuente
Puede usar una publicación sincrónica en las funciones beforeunload y unload. Especialmente si necesita datos del servidor. Es posible que no se complete ningún código asíncrono dentro de esos bloques cuando se descargue la página.
jemiloii
3

Me enfrenté al mismo problema, estaba bien para obtener su propio cuadro de diálogo con mi mensaje, pero el problema que enfrenté fue: 1) Estaba dando mensajes en todas las navegaciones. Lo quiero solo con un clic cercano. 2) con mi propio mensaje de confirmación si el usuario selecciona cancelar, todavía muestra el cuadro de diálogo predeterminado del navegador.

El siguiente es el código de soluciones que encontré, que escribí en mi página maestra.

function closeMe(evt) {
    if (typeof evt == 'undefined') {
        evt = window.event; }
    if (evt && evt.clientX >= (window.event.screenX - 150) &&
        evt.clientY >= -150 && evt.clientY <= 0) {
        return "Do you want to log out of your current session?";
    }
}
window.onbeforeunload = closeMe;
Imran Rizvi
fuente
1
 <script type="text/javascript">
        window.onbeforeunload = function(evt) {
            var message = 'Are you sure you want to leave?';
            if (typeof evt == 'undefined') {
                evt = window.event;
            }       
            if (evt) {
                evt.returnValue = message;
            }
            return message;
        } 
    </script>

consulte desde http://www.codeprojectdownload.com

Krishna Patel
fuente
1

¿Qué hay de usar la versión especializada del comando "unir"? Una vez que el controlador de eventos se ejecuta por primera vez, se elimina automáticamente como un controlador de eventos.

$(window).one("beforeunload", BeforeUnload);
Riga
fuente
bastante seguro de que esto es .on ("beforeunload"
Sergiu Mindras
2
@SergiuMindras no, es .one()- que une a un oyente de eventos para que escuche que el evento ocurra solo una vez: "Adjunte un controlador a un evento para los elementos. El controlador se ejecuta como máximo una vez por elemento por tipo de evento". api.jquery.com/one
Sean Kendle
0

Enfoque angular 9:

constructor() {
    window.addEventListener('beforeunload', (event: BeforeUnloadEvent) => {
     if (this.generatedBarcodeIndex) {
      event.preventDefault(); // for Firefox
      event.returnValue = ''; // for Chrome
      return '';
     }
     return false;
    });
   }

Soporte de navegadores y eliminación del mensaje personalizado:

  • Chrome eliminó la compatibilidad con el mensaje personalizado en 51 min.
  • Opera eliminó el soporte para el mensaje personalizado en ver 38 min.
  • Firefox eliminó la compatibilidad con el mensaje personalizado en la versión 44.0 min.
  • Safari eliminó la compatibilidad con el mensaje personalizado en la versión 9.1 min.
Istvan Dembrovszky
fuente