Как определить, перешел ли пользователь на страницу с помощью кнопки возврата?

90

Этот вопрос похож на « Отслеживать, когда пользователь нажимает кнопку возврата» в браузере , но не такой же ... У меня есть решение, и я публикую его здесь для справки и обратной связи. Если у кого-то есть варианты получше, я весь в уши!

Ситуация такова, что у меня есть страница с «редактированием на месте», а-ля flickr. Т.е. есть DIV "нажмите здесь, чтобы добавить описание", который при нажатии превращается в ТЕКСТАР с кнопками Сохранить и Отменить. Нажатие кнопки «Сохранить» отправляет данные на сервер для обновления базы данных и помещает новое описание в DIV вместо TEXTAREA. Если страница обновляется, новое описание отображается из базы данных с опцией «щелкните для редактирования». В наши дни это довольно стандартная штука для Web 2.0.

Проблема в том, что если:

  1. страница загружается без описания
  2. описание добавлено пользователем
  3. переход на страницу осуществляется щелчком по ссылке
  4. пользователь нажимает кнопку возврата

Затем отображается (из кеша браузера) версия страницы без динамически измененного DIV, содержащего новое описание.

Это довольно большая проблема, поскольку пользователь предполагает, что их обновление было потеряно, и не обязательно понимает, что им нужно обновить страницу, чтобы увидеть изменения.

Итак, возникает вопрос: как вы можете пометить страницу как измененную после ее загрузки, а затем определить, когда пользователь «возвращается к ней», и принудительно выполнить обновление в этой ситуации?

Том
источник
Чем это отличается от указанного вами вопроса?
innaM
вопрос похож, но я думаю, что среда и, следовательно, ответ другой, я могу ошибаться. Моя интерпретация того, какая проблема может быть решена с помощью другого решения, такова: пользователь нажимает вкладку на странице, загруженной с помощью ajax, затем другую вкладку и так далее. нажав кнопку «Назад», вы вернетесь на другую страницу, а не на предыдущую вкладку. они хотят вернуться к «истории ajax» в «истории страниц». по крайней мере, это мое впечатление о том, что должен делать Yahoo Browser History Manager. Я был после чего-то более простого.
Том
В принятом ответе описан ваш трюк с iframe.
innaM

Ответы:

79

Используйте скрытую форму. Данные формы сохраняются (обычно) в браузерах, когда вы перезагружаете или нажимаете кнопку «Назад» для возврата на страницу. На вашей странице (вероятно, внизу) будет следующее:

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

В вашем javascript вам понадобится следующее:

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, который обнюхивает форму, должен выполняться после того, как HTML полностью проанализирован, но вы можете поместить как форму, так и js в строку вверху страницы (секунда js), если задержка пользователя вызывает серьезную озабоченность.

Ник Уайт
источник
5
«Данные формы (как правило) сохраняются в браузерах» - можете ли вы расширить это? в некоторых браузерах он не сохраняется? или в каких ситуациях?
Том
4
На самом деле, позвольте мне перечислить все способы, которые, по моему мнению, могут пойти не так. (1) Opera сохраняет полное состояние страницы и не выполняет повторно js. (2) Если настройки кеша настроены так, чтобы страница загружалась с сервера при нажатии кнопки «Назад», данные формы могут быть потеряны, что нормально для целей этого вопроса. (3) Телефонные браузеры имеют гораздо меньший размер кеша, и браузер, возможно, уже удалил страницу из кеша (не проблема для этих целей).
Ник Уайт,
10
В некоторых браузерах скрытые теги не сохраняются. Таким образом, вы можете использовать скрытый текстовый тег вместо скрытого тега ввода. <input type = "text" value = "0" id = 'txt' name = 'txt' style = "display: none" />
саджит
2
Это отлично сработало для меня. Я уверен, что у него бывают ситуации, когда он выходит из строя, но каждое решение этой проблемы подойдет.
Joren
Он не работает на iPhone с браузером Safari, попробуйте, когда вы нажимаете кнопку «Назад», js больше не вызывается.
woheras
53

Вот очень простое современное решение этой старой проблемы.

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 в настоящее время поддерживается всеми основными браузерами.

Bassem
источник
Я не слышал об этом. Я только что нашел эту документацию ( developer.mozilla.org/en-US/docs/Web/API/Performance/navigation ), и мне было бы интересно услышать, как вы доработаете (например, зачем нужен, window.performance && window.performance.navigation.typeа не просто window.performance.navigation.TYPE_BACK_FORWARD?).
Райан
5
window.performance.navigation.TYPE_BACK_FORWARD- константа, которая всегда равна 2. Поэтому она используется только для сравнения.
Bassem
Performance.navigation поддерживается на всех устройствах, кроме andorid и Opera mini, но без проблем, все очень хорошо ( developer.mozilla.org/en-US/docs/Web/API/Performance/navigation )
Эльбаз
1
Похоже, performance.navigationэто устарело, и вместо этого предлагается использовать developer.mozilla.org/en-US/docs/Web/API/…
RubberDuckRabbit
1
Firefox v70 будет поддерживать performance.navigation = 2при нажатии кнопки «Назад». До v70 возвращал некорректно 1.
Энди Мерсер
13

Эта статья объясняет это. См. Код ниже: 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
Это хороший ответ, работает в последних версиях Chrome 40 / Firefox 35. Однако IE11 не поддерживает эти события.
Слава Абакумов
Согласно caniuse.com/#feat=page-transition-events события перехода страницы на самом деле поддерживаются в IE11 +. На данный момент глобальный охват этой функции составляет 88%,
Кристиан,
FYI, Chrome возвращает event.persisted= false, даже при нажатии кнопки возврата. Вы должны использовать window.performance.navigationили PerformanceNavigationTiming для Chrome.
Энди Мерсер
4

Для современных браузеров сейчас это кажется правильным:

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

По состоянию на январь 2020 года это все еще "экспериментальное", но, похоже, поддерживается хорошо.

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

getup8
источник
1
лучше использовать === вместо ==
Aboozar Rajabi
2

Как упоминалось выше, я нашел решение и публикую его здесь для справки и обратной связи.

Первый этап решения - добавить на страницу следующее:

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

Содержание fresh.htmlне имеет значения, поэтому достаточно следующего:

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

Когда код на стороне клиента обновляет страницу, он должен пометить модификацию следующим образом:

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.htmlвыполняет всю работу: при загрузке он вызывает reload_stale_pageфункцию, которая при необходимости обновляет страницу. При первой загрузке (то есть после внесения изменений reload_stale_pageфункция ничего не сделает).

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

Судя по моему (минимальному) тестированию на этом этапе, похоже, что это работает так, как хотелось. Я что-нибудь упустил?

Том
источник
Не знаю, но полностью не тестировал. getElementById не поддерживается в некоторых старых браузерах, но при необходимости его легко заменить на jquery или что-то еще.
Том
2

Вы можете решить эту проблему с помощью события onbeforeunload :

window.onbeforeunload = function () { }

Наличие пустой функции диспетчера событий onbeforeunload означает, что страница будет перестраиваться при каждом доступе к ней. Javascripts будет повторно запущен, серверные скрипты будут повторно запущены, страница будет построена так, как если бы пользователь нажал на нее в первый раз, даже если пользователь попал на страницу, просто нажав кнопку назад или вперед .

Вот полный код примера:

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

Попробуйте, уйдите с этой страницы, а затем вернитесь, используя кнопки «Назад» или «Вперед» в своем браузере.

Он отлично работает в IE, FF и Chrome.

Конечно, внутри функции myfun вы можете делать все, что захотите .

Дополнительная информация: http://www.hunlock.com/blogs/Mastering_The_Back_Button_With_Javascript

Nes
источник
У меня работает в Safari
Якоб
2

Вы можете использовать localStorage или sessionStorage ( http://www.w3schools.com/html/html5_webstorage.asp ) для установки флага (вместо использования скрытой формы).

перерезать
источник
это хороший обходной путь, но как вы это делаете, если он хочет обнаруживать это каждый раз, когда я открывал сайт и закрывал его, мне нужно определять, когда я открывался, и возвращаться перед закрытием
Эльбаз
1

Использование этой страницы, особенно с учетом комментария @sajith об ответе @Nick White и этой страницы: 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
источник
1

Вот версия jQuery. Я столкнулся с необходимостью использовать его несколько раз из-за того, как Safari Desktop / Mobile обрабатывает кеш, когда пользователь нажимает кнопку возврата.

$(window).bind("pageshow", function(event) {
    if (event.originalEvent.persisted) {
        // Loading from cache
    }
});
Уэс
источник
Насколько я могу судить, не работает в Chrome. Console.log запускается только в инструкции else (добавленной для тестирования) независимо от того, как я получаю доступ к странице.
Люк
0

Сегодня я столкнулся с аналогичной проблемой, и я решил ее с помощью localStorage (здесь с небольшим количеством 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 в основном:

На странице 1, когда пользователь отправляет форму, мы устанавливаем значение "steps" в localStorage, чтобы указать, какой шаг сделал пользователь. В то же время мы запускаем функцию с таймаутом, которая проверяет, было ли это значение изменено (например, 10 раз в секунду).

На странице 2 мы немедленно меняем указанное значение.

Таким образом, если пользователь использует кнопку «Назад», а браузер восстанавливает страницу 1 в том же состоянии, в котором она была, когда мы ее покинули, функция checkSteps все еще работает и может обнаружить, что значение в localStorage было изменено (и может предпринять соответствующие действия. ). Как только эта проверка выполнит свою задачу, нет необходимости продолжать ее выполнение, поэтому мы просто больше не используем setTimeout.

s427
источник