Cómo detectar el evento del Botón de Retroceso del Navegador-Cross Browser


¿Cómo se detecta definitivamente si el usuario ha pulsado o no el botón atrás en el navegador?

¿Cómo se impone el uso de un botón de retroceso dentro de una aplicación web de una sola página utilizando un sistema #URL?

¿Por qué demonios no los botones de retroceso del navegador activan sus propios eventos??

Author: Xarus, 2014-09-12

9 answers

(Nota: Según los comentarios de Sharky, he incluido código para detectar backspaces)

Por lo tanto, he visto estas preguntas con frecuencia en SO, y recientemente me he topado con el tema de controlar la funcionalidad del botón atrás. Después de unos días de buscar la mejor solución para mi aplicación (Una sola página con navegación Hash), se me ocurrió un sistema simple, cross-browser, sin biblioteca para detectar el botón atrás.

La mayoría de la gente recomienda usando:

window.onhashchange = function() {
 //blah blah blah
}

Sin embargo, esta función también se llamará cuando un usuario use el elemento on in-page que cambie el hash de ubicación. No es la mejor experiencia de usuario cuando el usuario hace clic y la página va hacia atrás o hacia adelante.

Para darle un esquema general de mi sistema, estoy llenando una matriz con hashes anteriores a medida que mi usuario se mueve a través de la interfaz. Se ve algo como esto:

function updateHistory(curr) {
    window.location.lasthash.push(window.location.hash);
    window.location.hash = curr;
}

Bastante sencillo. Hago esto para asegurar el soporte entre navegadores, también como soporte para navegadores más antiguos. Simplemente pase el nuevo hash a la función, y lo almacenará para usted y luego cambiará el hash (que luego se coloca en el historial del navegador).

También utilizo un botón de retroceso en la página que mueve al usuario entre las páginas utilizando el array lasthash. Se ve así:

function goBack() {
    window.location.hash = window.location.lasthash[window.location.lasthash.length-1];
    //blah blah blah
    window.location.lasthash.pop();
}

Así que esto moverá al usuario de nuevo al último hash, y eliminará ese último hash de la matriz (no tengo ningún botón de avance en este momento).

So. ¿Cómo puedo detectar si o no un usuario ha utilizado mi botón de retroceso en la página, o el botón del navegador?

Al principio miré window.onbeforeunload, pero en vano - eso solo se llama si el usuario va a cambiar de página. Esto no sucede en una aplicación de una sola página usando navegación hash.

Así que, después de investigar un poco más, vi recomendaciones para intentar establecer una variable de bandera. El problema con esto en mi caso, es que intentaría configurarlo, pero como todo es asíncrono, no siempre se establecería a tiempo para el si declaración en el cambio de hash. .onMouseDown no siempre fue llamado en click, y agregarlo a un onclick nunca lo activaría lo suficientemente rápido.

Esto es cuando empecé a mirar la diferencia entre document, y window. Mi solución final fue establecer el flag usando document.onmouseover, y deshabilitarlo usando document.onmouseleave.

Lo que sucede es que mientras el ratón del usuario está dentro del área del documento (leer: la página renderizada, pero excluyendo el marco del navegador), mi booleano se establece en true. Tan pronto como el ratón sale del área del documento, el booleano cambia a false.

De Esta manera, puedo cambiar mi window.onhashchange a:

window.onhashchange = function() {
    if (window.innerDocClick) {
        window.innerDocClick = false;
    } else {
        if (window.location.hash != '#undefined') {
            goBack();
        } else {
            history.pushState("", document.title, window.location.pathname);
            location.reload();
        }
    }
}

Anotará el cheque para #undefined. Esto se debe a que si no hay un historial disponible en mi matriz, devuelve undefined. Uso esto para preguntar al usuario si quiere salir usando un evento window.onbeforeunload.

Así que, en resumen, y para las personas que no están necesariamente utilizando un botón de retroceso en la página o una matriz para almacenar el historial:

document.onmouseover = function() {
    //User's mouse is inside the page.
    window.innerDocClick = true;
}

document.onmouseleave = function() {
    //User's mouse has left the page.
    window.innerDocClick = false;
}

window.onhashchange = function() {
    if (window.innerDocClick) {
        //Your own in-page mechanism triggered the hash change
    } else {
        //Browser back button was clicked
    }
}

, Y ahí lo tienen. simple, forma de tres partes para detectar el uso del botón atrás vs elementos en la página con respecto a la navegación hash.

EDITAR:

Para asegurarse de que el usuario no use backspace para activar el evento back, también puede incluir lo siguiente (Gracias a @thetoolman en esta pregunta):

$(function(){
    /*
     * this swallows backspace keys on any non-input element.
     * stops backspace -> back
     */
    var rx = /INPUT|SELECT|TEXTAREA/i;

    $(document).bind("keydown keypress", function(e){
        if( e.which == 8 ){ // 8 == backspace
            if(!rx.test(e.target.tagName) || e.target.disabled || e.target.readOnly ){
                e.preventDefault();
            }
        }
    });
});
 125
Author: Xarus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-18 08:40:44

Puedes probar popstate controlador de eventos, por ejemplo:

window.addEventListener('popstate', function(event) {
    // The popstate event is fired each time when the current history entry changes.

    var r = confirm("You pressed a Back button! Are you sure?!");

    if (r == true) {
        // Call Back button programmatically as per user confirmation.
        history.back();
        // Uncomment below line to redirect to the previous page instead.
        // window.location = document.referrer // Note: IE11 is not supporting this.
    } else {
        // Stay on the current page.
        history.pushState(null, null, window.location.pathname);
    }

    history.pushState(null, null, window.location.pathname);

}, false);

Nota: Para obtener los mejores resultados, debe cargar este código solo en páginas específicas donde desea implementar la lógica para evitar otros problemas inesperados.

El evento popstate se activa cada vez que cambia la entrada del historial actual (el usuario navega a un nuevo estado). Eso sucede cuando el usuario hace clic en los botones de atrás/adelante del navegador o cuando history.back(), history.forward(), history.go() los métodos son programáticos called.

La propiedad event.state is del evento es igual al objeto history state.

Para la sintaxis de jQuery, envuélvala (para agregar un oyente uniforme después de que el documento esté listo):

(function($) {
  // Above code here.
})(jQuery);

Véase también: ventana.onpopstate on page load


Vea también los ejemplos en Aplicaciones de una sola página y HTML5 pushState página:

<script>
// jQuery
$(window).on('popstate', function (e) {
    var state = e.originalEvent.state;
    if (state !== null) {
        //load content with ajax
    }
});

// Vanilla javascript
window.addEventListener('popstate', function (e) {
    var state = e.state;
    if (state !== null) {
        //load content with ajax
    }
});
</script>

Esto debería ser compatible con Chrome 5+, Firefox 4+, IE 10+, Safari 6+, Opera 11.5+ y similares.

 46
Author: kenorb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:50

Había estado luchando con este requisito durante bastante tiempo y tomé algunas de las soluciones anteriores para implementarlo. Sin embargo, me topé con una observación y parece que funciona a través de Chrome, Firefox y navegadores Safari + Android y iPhone

En la carga de la página:

window.history.pushState({page: 1}, "", "");

window.onpopstate = function(event) {

  // "event" object seems to contain value only when the back button is clicked
  // and if the pop state event fires due to clicks on a button
  // or a link it comes up as "undefined" 

  if(event){
    // Code to handle back button or prevent from navigation
  }
  else{
    // Continue user action through link or button
  }
}

Hazme saber si esto ayuda. Si me falta algo, estaré encantado de entenderlo.

 9
Author: Itzmeygu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-22 11:56:11

Navegador: https://jsfiddle.net/Limitlessisa/axt1Lqoz/

Para el control móvil: https://jsfiddle.net/Limitlessisa/axt1Lqoz/show /

$(document).ready(function() {
  $('body').on('click touch', '#share', function(e) {
    $('.share').fadeIn();
  });
});

// geri butonunu yakalama
window.onhashchange = function(e) {
  var oldURL = e.oldURL.split('#')[1];
  var newURL = e.newURL.split('#')[1];

  if (oldURL == 'share') {
    $('.share').fadeOut();
    e.preventDefault();
    return false;
  }
  //console.log('old:'+oldURL+' new:'+newURL);
}
.share{position:fixed; display:none; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,.8); color:white; padding:20px;
<!DOCTYPE html>
<html>

<head>
    <title>Back Button Example</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

</head>

<body style="text-align:center; padding:0;">
    <a href="#share" id="share">Share</a>
    <div class="share" style="">
        <h1>Test Page</h1>
        <p> Back button press please for control.</p>
    </div>
</body>

</html>
 2
Author: Limitless isa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-19 15:38:03

Aquí está mi opinión. La suposición es, cuando la URL cambia pero no tiene clic dentro de la document detectado, es un navegador atrás (sí, o adelante). Un clic de usuario se restablece después de 2 segundos para hacer que esto funcione en las páginas que cargan contenido a través de Ajax:

(function(window, $) {
  var anyClick, consoleLog, debug, delay;
  delay = function(sec, func) {
    return setTimeout(func, sec * 1000);
  };
  debug = true;
  anyClick = false;
  consoleLog = function(type, message) {
    if (debug) {
      return console[type](message);
    }
  };
  $(window.document).click(function() {
    anyClick = true;
    consoleLog("info", "clicked");
    return delay(2, function() {
      consoleLog("info", "reset click state");
      return anyClick = false;
    });
  });
  return window.addEventListener("popstate", function(e) {
    if (anyClick !== true) {
      consoleLog("info", "Back clicked");
      return window.dataLayer.push({
        event: 'analyticsEvent',
        eventCategory: 'test',
        eventAction: 'test'
      });
    }
  });
})(window, jQuery);
 2
Author: TomTom101,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-17 10:37:50

El documento.mouseover no funciona para IE y FireFox. Sin embargo he intentado esto :

$(document).ready(function () {
  setInterval(function () {
    var $sample = $("body");
    if ($sample.is(":hover")) {
      window.innerDocClick = true;
    } else {
      window.innerDocClick = false;
    }
  });

});

window.onhashchange = function () {
  if (window.innerDocClick) {
    //Your own in-page mechanism triggered the hash change
  } else {
    //Browser back or forward button was pressed
  }
};

Esto funciona para Chrome e IE y no FireFox. Sigo trabajando para que FireFox funcione correctamente. Cualquier forma fácil de detectar el botón de retroceso/avance del navegador es bienvenida, no particularmente en jQuery, sino también en AngularJS o Javascript simple.

 1
Author: Dhruv Gupta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-21 07:14:46

Lo resolví haciendo un seguimiento del evento original que activó el hashchange (ya sea un golpe, un clic o una rueda), para que el evento no se confundiera con un simple aterrizaje en la página, y usando una bandera adicional en cada uno de mis enlaces de eventos. El navegador no volverá a configurar la bandera en false al presionar el botón atrás:

var evt = null,
canGoBackToThePast = true;

$('#next-slide').on('click touch', function(e) {
    evt = e;
    canGobackToThePast = false;
    // your logic (remember to set the 'canGoBackToThePast' flag back to 'true' at the end of it)
}
 0
Author: Niccolò Mineo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-14 17:11:02
 <input style="display:none" id="__pageLoaded" value=""/>


 $(document).ready(function () {
        if ($("#__pageLoaded").val() != 1) {

            $("#__pageLoaded").val(1);


        } else {
            shared.isBackLoad = true;
            $("#__pageLoaded").val(1);  

            // Call any function that handles your back event

        }
    });
 0
Author: Ashwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-17 19:24:18

Probé las opciones anteriores, pero ninguna de ellas funciona para mí. Aquí está la solución

if(window.event)
   {
        if(window.event.clientX < 40 && window.event.clientY < 0)
        {
            alert("Browser back button is clicked...");
        }
        else
        {
            alert("Browser refresh button is clicked...");
        }
    }

Consulte este enlace http://www.codeproject.com/Articles/696526/Solution-to-Browser-Back-Button-Click-Event-Handli para más detalles

 -13
Author: Sures Ramar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-12 12:48:27