Come faccio a rilevare se un utente è arrivato a una pagina utilizzando il pulsante Indietro?


90

Questa domanda è simile a Traccia quando l'utente preme il pulsante Indietro sul browser , ma non è la stessa ... Ho una soluzione e la sto pubblicando qui per riferimento e feedback. Se qualcuno ha opzioni migliori, sono tutt'orecchi!

La situazione è che ho una pagina con una "modifica sul posto", a la flickr. Cioè c'è un DIV "clicca qui per aggiungere una descrizione", che se cliccato si trasforma in una TEXTAREA con i pulsanti Salva e Annulla. Facendo clic su Salva, i dati vengono inviati al server per aggiornare il database e la nuova descrizione viene inserita nel DIV al posto di TEXTAREA. Se la pagina viene aggiornata, la nuova descrizione viene visualizzata dal database con un'opzione "fai clic per modificare". Roba web 2.0 abbastanza standard in questi giorni.

Il problema è che se:

  1. la pagina viene caricata senza la descrizione
  2. una descrizione viene aggiunta dall'utente
  3. si esce dalla pagina facendo clic su un collegamento
  4. l'utente fa clic sul pulsante Indietro

Quindi ciò che viene visualizzato (dalla cache del browser) è la versione della pagina senza il DIV modificato dinamicamente contenente la nuova descrizione.

Questo è un problema abbastanza grosso in quanto l'utente presume che il proprio aggiornamento sia stato perso e non capirà necessariamente che è necessario aggiornare la pagina per vedere le modifiche.

Quindi, la domanda è: come puoi contrassegnare una pagina come modificata dopo che è stata caricata e quindi rilevare quando l'utente "torna ad essa" e forzare un aggiornamento in quella situazione?


In che modo è diverso dalla domanda che hai citato?
innaM

la domanda è simile, ma penso che l'ambiente e quindi la risposta sia diversa, potrei sbagliarmi. la mia interpretazione di quale potrebbe essere un problema che verrebbe risolto con l'altra soluzione è: l'utente fa clic su una scheda in una pagina caricata da ajax, quindi su un'altra scheda e così via. facendo clic sul pulsante Indietro si torna a una pagina diversa, non alla scheda precedente. vogliono tornare indietro attraverso la "cronologia ajax" all'interno della "cronologia della pagina". almeno questa è la mia impressione di ciò che dovrebbe fare Yahoo Browser History Manager. stavo cercando qualcosa di un po 'più semplice.
Tom

La risposta accettata presenta il tuo trucco iframe.
innaM

Risposte:


79

Usa un modulo nascosto. I dati del modulo vengono conservati (in genere) nei browser quando ricarichi o premi il pulsante Indietro per tornare a una pagina. Quanto segue va nella tua pagina (probabilmente nella parte inferiore):

<form name="ignore_me">
    <input type="hidden" id="page_is_dirty" name="page_is_dirty" value="0" />
</form>

Nel tuo javascript, avrai bisogno di quanto segue:

var dirty_bit = document.getElementById('page_is_dirty');
if (dirty_bit.value == '1') window.location.reload();
function mark_page_dirty() {
    dirty_bit.value = '1';
}

Il js che annusa il form deve essere eseguito dopo che l'html è stato completamente analizzato, ma potresti mettere sia il form che il js inline nella parte superiore della pagina (js secondo) se la latenza dell'utente è un serio problema.


5
"I dati dei moduli vengono conservati (in genere) nei browser": sei in grado di ampliarlo? non è conservato in alcuni browser? o in alcune situazioni?
Tom

4
In realtà, lasciatemi elencare tutti i modi in cui posso pensare che questo vada storto. (1) Opera mantiene lo stato completo della pagina e non eseguirà nuovamente js. (2) Se le impostazioni della cache vengono modificate per forzare il caricamento della pagina dal server sul pulsante Indietro, i dati del modulo potrebbero andare persi, il che va bene ai fini di questa domanda. (3) I browser del telefono hanno una cache molto più piccola e il browser potrebbe aver già eliminato la pagina dalla cache (non è un problema per questi scopi).
Nick White

10
In alcuni browser i tag nascosti non vengono conservati. Quindi potresti usare un tag di testo nascosto invece di usare un tag di input nascosto. <input type = "text" value = "0" id = 'txt' name = 'txt' style = "display: none" />
sajith

2
Questo ha funzionato benissimo per me. Sono sicuro che ci sono situazioni in cui si rompe, ma ogni soluzione lo farà per questo problema.
Joren

Non funziona su iPhone con browser Safari, provalo, quando fai clic sul pulsante Indietro, js non viene richiamato.
woheras

52

Ecco una soluzione moderna molto semplice a questo vecchio problema.

if (window.performance && window.performance.navigation.type === window.performance.navigation.TYPE_BACK_FORWARD) {
    alert('Got here using the browser "Back" or "Forward" button.');
}

window.performance è attualmente supportato da tutti i principali browser.


Non ne avevo sentito parlare. Ho appena trovato questa documentazione ( developer.mozilla.org/en-US/docs/Web/API/Performance/navigation ) e sarei interessato a sentirti approfondire (es. Perché è necessario window.performance && window.performance.navigation.typeinvece che solo window.performance.navigation.TYPE_BACK_FORWARD?).
Ryan

5
window.performance.navigation.TYPE_BACK_FORWARDè una costante che è sempre uguale a 2. Quindi viene utilizzata solo per il confronto.
Bassem

Performance.navigation supportato su tutti i dispositivi eccetto andorid e opera mini, ma nessun problema è molto carino ( developer.mozilla.org/en-US/docs/Web/API/Performance/navigation )
Elbaz

1
Sembra che performance.navigationsia deprecato e si consiglia di utilizzare invece developer.mozilla.org/en-US/docs/Web/API/…
RubberDuckRabbit,

1
Firefox v70 supporterà performance.navigation = 2per i pulsanti Indietro. Fino alla v70, restituiva erroneamente 1.
Andy Mercer

13

Questo articolo lo spiega. Vedi il codice seguente: http://www.webkit.org/blog/516/webkit-page-cache-ii-the-unload-event/

<html>
    <head>
        <script>

            function pageShown(evt){
                if (evt.persisted) {
                    alert("pageshow event handler called.  The page was just restored from the Page Cache (eg. From the Back button.");
                } else {
                    alert("pageshow event handler called for the initial load.  This is the same as the load event.");
                }
            }

            function pageHidden(evt){
                if (evt.persisted) {
                    alert("pagehide event handler called.  The page was suspended and placed into the Page Cache.");
                } else {
                    alert("pagehide event handler called for page destruction.  This is the same as the unload event.");
                }
            }

            window.addEventListener("pageshow", pageShown, false);
            window.addEventListener("pagehide", pageHidden, false);

        </script>
    </head>
    <body>
        <a href="http://www.webkit.org/">Click for WebKit</a>
    </body>
</html>

1
Questa è una buona risposta, funziona nell'ultimo Chrome 40 / Firefox 35. IE11 non supporta questi eventi, però.
Slava Abakumov

Secondo caniuse.com/#feat=page-transition-events gli eventi di transizione della pagina sono effettivamente supportati in IE11 + Mentre parliamo, c'è l'88% di copertura globale della funzione
Cristian

Cordiali saluti, Chrome restituisce event.persisted= false, anche quando si preme dal pulsante Indietro. Devi utilizzare window.performance.navigationo PerformanceNavigationTiming per Chrome.
Andy Mercer

4

Per i browser moderni, questo sembra essere il modo giusto ora:

const perfEntries = performance.getEntriesByType('navigation');
if (perfEntries.length && perfEntries[0].type === 'back_forward') {
  console.log('User got here from Back or Forward button.');
}

Questo è ancora "sperimentale" a partire da gennaio 2020, ma sembra essere supportato bene.

https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigationTiming


1
meglio usare === invece di ==
Aboozar Rajabi il

2

Come accennato in precedenza, avevo trovato una soluzione e la sto pubblicando qui per riferimento e feedback.

La prima fase della soluzione è aggiungere quanto segue alla pagina:

<!-- at the top of the content page -->
<IFRAME id="page_is_fresh" src="fresh.html" style="display:none;"></IFRAME>
<SCRIPT style="text/javascript">
  function reload_stale_page() { location.reload(); }
</SCRIPT>

I contenuti di fresh.htmlnon sono importanti, quindi dovrebbe essere sufficiente quanto segue:

<!-- fresh.html -->
<HTML><BODY></BODY></HTML>

Quando il codice lato client aggiorna la pagina, è necessario contrassegnare la modifica come segue:

function trigger_reload_if_user_clicks_back_button()
{
  // "dis-arm" the reload stale page function so it doesn't fire  
  // until the page is reloaded from the browser's cache
  window.reload_stale_page = function(){};

  // change the IFRAME to point to a page that will reload the 
  // page when it loads
  document.getElementById("page_is_fresh").src = "stale.html";
}

stale.htmlfa tutto il lavoro: quando viene caricato chiamerà la reload_stale_pagefunzione che aggiornerà la pagina se necessario. La prima volta che viene caricato (cioè dopo che la modifica è stata effettuata, la reload_stale_pagefunzione non farà nulla).

<!-- stale.html -->
<HTML><BODY>
<SCRIPT type="text/javascript">window.parent.reload_stale_page();</SCRIPT>
</BODY></HTML>

Dal mio (minimo) test in questa fase, sembra funzionare come desiderato. Ho trascurato qualcosa?


Non che io sappia, ma non l'ho testato completamente. getElementById non è supportato in alcuni browser meno recenti, ma è facile sostituirlo con jquery o qualcosa del genere se necessario.
Tom

2

Puoi risolverlo usando l' evento onbeforeunload :

window.onbeforeunload = function () { }

Avere una funzione di gestione eventi vuota onbeforeunload significa che la pagina verrà ricostruita ogni volta che si accede. Javascript verrà rieseguito, gli script lato server verranno rieseguiti, la pagina verrà costruita come se l'utente la stesse premendo per la prima volta, anche se l'utente è arrivato alla pagina semplicemente premendo il pulsante Indietro o Avanti .

Ecco il codice completo di un esempio:

<html>
<head>
<title>onbeforeunload.html</title>
<script>
window.onbeforeunload = function () { }

function myfun()
{
   alert("The page has been refreshed.");
}
</script>

<body onload="myfun()">

Hello World!<br>

</body>
</html>

Provalo, esci da questa pagina e torna indietro utilizzando i pulsanti "Indietro" o "Avanti" nel browser.

Funziona bene in IE, FF e Chrome.

Ovviamente puoi fare quello che vuoi all'interno della funzione myfun .

Maggiori informazioni: http://www.hunlock.com/blogs/Mastering_The_Back_Button_With_Javascript


Funziona in Safari per me
Jakob

2

È possibile utilizzare localStorage o sessionStorage ( http://www.w3schools.com/html/html5_webstorage.asp ) per impostare un flag (invece di utilizzare un modulo nascosto).


è una bella soluzione, ma come fai se vuole rilevarlo ogni volta, quando ho aperto il sito e l'ho chiuso, ho bisogno di rilevare solo quando ho aperto e tornare indietro prima di chiudere
Elbaz

1

Utilizzando questa pagina, in particolare incorporando il commento di @sajith sulla risposta di @Nick White e questa pagina: http://www.mrc-productivity.com/techblog/?p=1235

<form name="ignore_me" style="display:none">
    <input type="text" id="reloadValue" name="reloadValue" value="" />
</form>

$(function ()
{
    var date = new Date();
    var time = date.getTime();

    if ($("#reloadValue").val().length === 0)
    {
        $("#reloadValue").val(time);
    }
    else
    {
        $("#reloadValue").val("");
        window.location.reload();
    }
});

1

Ecco una versione di jQuery. Mi è capitato di doverlo usare un paio di volte a causa del modo in cui Safari desktop / mobile gestisce la cache quando un utente preme il pulsante Indietro.

$(window).bind("pageshow", function(event) {
    if (event.originalEvent.persisted) {
        // Loading from cache
    }
});

Non sembra funzionare in Chrome per quanto ne so. Un console.log viene attivato solo nell'istruzione else (aggiunta per il test) indipendentemente da come accedo alla pagina.
Luca

0

Oggi ho riscontrato un problema simile e l'ho risolto con localStorage (qui con un po 'di jQuery):

$(function() {

    if ($('#page1').length) {
        // this code must only run on page 1

        var checkSteps = function () {
            if (localStorage.getItem('steps') == 'step2') {
                // if we are here, we know that:
                // 1. the user is on page 1
                // 2. he has been on page 2
                // 3. this function is running, which means the user has submitted the form
                // 4. but steps == step2, which is impossible if the user has *just* submitted the form
                // therefore we know that he has come back, probably using the back button of his browser
                alert('oh hey, welcome back!');
            } else {
                setTimeout(checkSteps, 100);
            }
        };

        $('#form').on('submit', function (e) {
            e.preventDefault();
            localStorage.setItem('steps', 'step1'); // if "step1", then we know the user has submitted the form
            checkOrderSteps();
            // ... then do what you need to submit the form and load page 2
        });
    }

    if ($('#page2').length) {
        // this code must only run on page 2
        localStorage.setItem('steps', 'step2');
    }

});

Si fondamentalmente:

A pagina 1, quando l'utente invia il modulo, impostiamo un valore "steps" in localStorage per indicare quale passaggio ha eseguito l'utente. Allo stesso tempo, lanciamo una funzione con timeout che controllerà se questo valore è stato modificato (es. 10 volte / secondo).

A pagina 2 cambiamo immediatamente detto valore.

Quindi, se l'utente utilizza il pulsante Indietro e il browser ripristina la pagina 1 nello stato esatto in cui era quando l'abbiamo lasciata, la funzione checkSteps è ancora in esecuzione ed è in grado di rilevare che il valore in localStorage è stato modificato (e può intraprendere l'azione appropriata ). Una volta che questo controllo ha soddisfatto il suo scopo, non è necessario continuare a eseguirlo, quindi semplicemente non usiamo più setTimeout.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.