Jak sprawdzić, czy użytkownik dostał się na stronę za pomocą przycisku Wstecz?

90

To pytanie jest podobne do opcji Śledź, kiedy użytkownik kliknie przycisk Wstecz w przeglądarce , ale nie to samo ... Mam rozwiązanie i zamieszczam je tutaj w celach informacyjnych i zwrotnych. Jeśli ktoś ma jakieś lepsze opcje, mam uszy!

Sytuacja jest taka, że ​​mam stronę z "edycją w miejscu" a la flickr. To znaczy „kliknij tutaj, aby dodać opis” DIV, który po kliknięciu zamienia się w TEXTAREA z przyciskami Zapisz i Anuluj. Kliknięcie Zapisz powoduje wysłanie danych do serwera w celu aktualizacji bazy danych i umieszczenie nowego opisu w DIV w miejsce TEXTAREA. Jeśli strona zostanie odświeżona, nowy opis zostanie wyświetlony z bazy danych z opcją „kliknij, aby edytować”. Dość standardowe rzeczy w web 2.0 w dzisiejszych czasach.

Problem polega na tym, że jeśli:

  1. strona jest ładowana bez opisu
  2. opis jest dodawany przez użytkownika
  3. strona jest opuszczana przez kliknięcie łącza
  4. użytkownik klika przycisk Wstecz

Wówczas wyświetlana jest (z pamięci podręcznej przeglądarki) wersja strony bez dynamicznie modyfikowanego DIV zawierającego nowy opis.

Jest to dość duży problem, ponieważ użytkownik zakłada, że ​​jego aktualizacja została utracona i niekoniecznie zrozumie, że musi odświeżyć stronę, aby zobaczyć zmiany.

A więc pytanie brzmi: jak oflagować stronę jako modyfikowaną po jej załadowaniu, a następnie wykryć, kiedy użytkownik „wraca do niej” i wymusić odświeżenie w takiej sytuacji?

Tomek
źródło
Czym to się różni od zadanego przez Ciebie pytania?
innaM
pytanie jest podobne, ale myślę, że środowisko i stąd odpowiedź jest inna, mogę się mylić. moja interpretacja tego, jaki problem może być rozwiązany za pomocą innego rozwiązania, jest następująca: użytkownik klika kartę na stronie ładowanej przez ajax, a następnie inną kartę i tak dalej. kliknięcie przycisku Wstecz spowoduje powrót do innej strony, a nie do poprzedniej karty. chcą przejść wstecz przez „historię ajax” w „historii strony”. przynajmniej takie mam wrażenie na temat tego, co powinien zrobić Menedżer historii przeglądarki Yahoo. szukałem czegoś bardziej podstawowego.
Tom
Zaakceptowana odpowiedź zawiera sztuczkę iframe.
innaM

Odpowiedzi:

79

Użyj ukrytego formularza. Dane formularzy są zachowywane (zazwyczaj) w przeglądarkach po ponownym załadowaniu lub naciśnięciu przycisku Wstecz w celu powrotu do strony. Następujące informacje znajdują się na Twojej stronie (prawdopodobnie u dołu):

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

W swoim javascript będziesz potrzebować:

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

Js, który wykrywa formularz, musi zostać wykonany po całkowitym przeanalizowaniu kodu HTML, ale możesz umieścić zarówno formularz, jak i js na górze strony (sekunda js), jeśli opóźnienie użytkownika jest poważnym problemem.

Nick White
źródło
5
„Dane formularzy są zachowywane (zazwyczaj) w przeglądarkach” - czy możesz to rozwinąć? czy nie jest zachowywany w niektórych przeglądarkach? czy w niektórych sytuacjach?
Tom
4
Właściwie, pozwólcie, że wymienię wszystkie sposoby, w jakie myślę, że to się nie udaje. (1) Opera zachowuje pełny stan strony i nie wykonuje ponownie js. (2) Jeśli ustawienia pamięci podręcznej zostaną zmienione, aby wymusić załadowanie strony z serwera po naciśnięciu przycisku Wstecz, dane formularza mogą zostać utracone, co jest w porządku do celów tego pytania. (3) Przeglądarki telefonów mają znacznie mniejszą pamięć podręczną, a przeglądarka mogła już usunąć stronę z pamięci podręcznej (nie stanowi to problemu do tych celów).
Nick White,
10
W niektórych przeglądarkach ukryte tagi nie są zachowywane. Możesz więc użyć ukrytego tagu tekstowego zamiast używać ukrytego tagu wejściowego. <input type = "text" value = "0" id = 'txt' name = 'txt' style = "display: none" />
sajith
2
To działało świetnie dla mnie. Jestem pewien, że zdarzają się sytuacje, w których się psuje, ale każde rozwiązanie będzie dla tego problemu.
Joren
Nie działa na iPhonie z przeglądarką Safari, spróbuj, po kliknięciu przycisku Wstecz js nie jest ponownie wywoływany.
woheras
54

Oto bardzo proste, nowoczesne rozwiązanie tego starego problemu.

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 jest obecnie obsługiwane przez wszystkie główne przeglądarki.

Bassem
źródło
Nie słyszałem o tym. Właśnie znalazłem tę dokumentację ( developer.mozilla.org/en-US/docs/Web/API/Performance/navigation ) i chciałbym usłyszeć, jak rozwiążesz tę kwestię (np. Dlaczego potrzebujesz window.performance && window.performance.navigation.typezamiast tylko window.performance.navigation.TYPE_BACK_FORWARD?).
Ryan
5
window.performance.navigation.TYPE_BACK_FORWARDjest stałą, która jest zawsze równa 2. Jest więc używana tylko do porównania.
Bassem
Performance.navigation obsługiwane na wszystkich urządzeniach z wyjątkiem andorid i opera mini, ale nie ma problemu, jest bardzo ładne ( developer.mozilla.org/en-US/docs/Web/API/Performance/navigation )
Elbaz
1
Wygląda na performance.navigationto, że jest przestarzały i sugeruje się, aby zamiast niego użyć developer.mozilla.org/en-US/docs/Web/API/ ...
RubberDuckRabbit,
1
Firefox v70 będzie obsługiwał performance.navigation = 2dla kliknięć przycisku wstecz. Aż do wersji 70 zwracał nieprawidłowo 1.
Andy Mercer
13

Ten artykuł wyjaśnia to. Zobacz poniższy kod: 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>
Drew Baker
źródło
1
To dobra odpowiedź, działa w najnowszym Chrome 40 / Firefox 35. IE11 nie obsługuje jednak tych zdarzeń.
Slava Abakumov
Według caniuse.com/#feat=page-transition-events zdarzenia związane z przenoszeniem stron są w rzeczywistości obsługiwane w IE11 +. Jak mówimy, funkcja ma 88% globalnego zasięgu
Cristian
FYI, Chrome zwraca event.persisted= false, nawet po naciśnięciu przycisku Wstecz. Musisz użyć window.performance.navigationlub PerformanceNavigationTiming dla Chrome.
Andy Mercer
4

W przypadku nowoczesnych przeglądarek wydaje się to teraz właściwe:

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

Jest to nadal „eksperymentalne” od stycznia 2020 r., Ale wydaje się, że jest dobrze obsługiwane.

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

getup8
źródło
1
lepiej użyć === zamiast ==
Aboozar Rajabi
2

Jak wspomniano powyżej, znalazłem rozwiązanie i zamieszczam je tutaj w celach informacyjnych i zwrotnych.

Pierwszym etapem rozwiązania jest dodanie do strony:

<!-- 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>

Treść fresh.htmlnie jest ważna, więc wystarczy:

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

Gdy kod po stronie klienta aktualizuje stronę, musi oznaczyć modyfikację w następujący sposób:

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.htmlwykonuje całą pracę: po załadowaniu wywoła reload_stale_pagefunkcję, która w razie potrzeby odświeży stronę. Gdy jest ładowany po raz pierwszy (tj. Po dokonaniu modyfikacji reload_stale_pagefunkcja nic nie zrobi).

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

Z moich (minimalnych) testów na tym etapie wynika, że ​​działa to zgodnie z oczekiwaniami. Czy coś przeoczyłem?

Tomek
źródło
Nie żebym o tym wiedział, ale nie przetestowałem go w pełni. Funkcja getElementById nie jest obsługiwana w niektórych starszych przeglądarkach, ale w razie potrzeby można ją łatwo zamienić na jquery lub coś podobnego.
Tom
2

Możesz rozwiązać ten problem za pomocą zdarzenia onbeforeunload :

window.onbeforeunload = function () { }

Posiadanie pustej funkcji menedżera zdarzeń przed załadowaniem oznacza, że ​​strona zostanie odbudowana za każdym razem, gdy zostanie uzyskana. Skrypty JavaScript zostaną ponownie uruchomione, skrypty po stronie serwera zostaną ponownie uruchomione, strona zostanie zbudowana tak, jakby użytkownik klikał ją po raz pierwszy, nawet jeśli użytkownik dostał się na stronę, naciskając przycisk Wstecz lub Dalej .

Oto pełny kod przykładu:

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

Wypróbuj, opuść tę stronę, a następnie wróć za pomocą przycisków „Wstecz” lub „Dalej” w przeglądarce.

Działa dobrze w IE, FF i Chrome.

Oczywiście możesz robić, co chcesz, korzystając z funkcji myfun .

Więcej informacji: http://www.hunlock.com/blogs/Mastering_The_Back_Button_With_Javascript

Nes
źródło
Działa dla mnie w Safari
Jakob
2

Możesz użyć localStorage lub sessionStorage ( http://www.w3schools.com/html/html5_webstorage.asp ), aby ustawić flagę (zamiast używać ukrytego formularza).

rerezz
źródło
to fajne obejście, ale jak to zrobić, jeśli chce to wykryć za każdym razem, kiedy otworzyłem witrynę i zamknąłem ją, muszę wykryć, kiedy otworzyłem i wrócić przed zamknięciem
Elbaz
1

Korzystanie z tej strony, w szczególności uwzględnienie komentarza @sajith na temat odpowiedzi @Nick White i tej strony: 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();
    }
});
nmit026
źródło
1

Oto wersja jQuery. Kilkakrotnie musiałem go użyć ze względu na sposób, w jaki Safari na komputer / telefon obsługuje pamięć podręczną, gdy użytkownik naciśnie przycisk Wstecz.

$(window).bind("pageshow", function(event) {
    if (event.originalEvent.persisted) {
        // Loading from cache
    }
});
Wes
źródło
1
O ile wiem, nie działa w Chrome. Konsola.log jest wyzwalana tylko w instrukcji else (dodanej do testowania), niezależnie od tego, w jaki sposób uzyskuję dostęp do strony.
Łukasz
0

Dziś napotkałem podobny problem i rozwiązałem go przy pomocy localStorage (tutaj z odrobiną 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');
    }

});

Zasadniczo:

Na stronie 1, gdy użytkownik przesyła formularz, ustawiamy wartość „steps” w localStorage, aby wskazać, jaki krok wykonał użytkownik. Jednocześnie uruchamiamy funkcję z limitem czasu, która sprawdzi, czy ta wartość została zmieniona (np. 10 razy / sekundę).

Na stronie 2 natychmiast zmieniamy wspomnianą wartość.

Jeśli więc użytkownik użyje przycisku Wstecz, a przeglądarka przywróci stronę 1 dokładnie w takim stanie, w jakim ją opuściliśmy, funkcja checkSteps nadal działa i jest w stanie wykryć, że wartość w localStorage została zmieniona (i może podjąć odpowiednią akcję ). Gdy to sprawdzenie spełni swoje zadanie, nie ma potrzeby kontynuowania go, więc po prostu nie używamy już setTimeout.

s427
źródło