Como posso detectar se um usuário chegou a uma página usando o botão Voltar?


90

Esta questão é semelhante a Rastrear quando o usuário pressiona o botão Voltar no navegador , mas não é o mesmo ... Tenho uma solução e a estou postando aqui para referência e feedback. Se alguém tiver opções melhores, sou todo ouvidos!

A situação é que tenho uma página com uma "edição no local", a la flickr. Ou seja, há um DIV "clique aqui para adicionar uma descrição", que quando clicado se transforma em uma TEXTAREA com os botões Salvar e Cancelar. Clicar em Salvar envia os dados para o servidor para atualizar o banco de dados e coloca a nova descrição no DIV no lugar de TEXTAREA. Se a página for atualizada, a nova descrição será exibida no banco de dados com uma opção "clique para editar". Coisas razoavelmente normais da Web 2.0 atualmente.

O problema é que se:

  1. a página é carregada sem a descrição
  2. uma descrição é adicionada pelo usuário
  3. a página é navegada para fora clicando em um link
  4. o usuário clica no botão Voltar

Então, o que é exibido (do cache do navegador) é a versão da página sem o DIV modificado dinamicamente contendo a nova descrição.

Este é um problema bastante grande, pois o usuário presume que sua atualização foi perdida e não necessariamente entenderá que precisa atualizar a página para ver as mudanças.

Portanto, a pergunta é: como você pode sinalizar uma página como modificada após ser carregada e, em seguida, detectar quando o usuário "volta para ela" e forçar uma atualização nessa situação?


Como isso é diferente da pergunta que você citou?
— innaM

a pergunta é semelhante, mas acho que o ambiente e, portanto, a resposta é diferente, posso estar errado. minha interpretação de qual pode ser um problema que seria resolvido com a outra solução é: o usuário clica em uma guia em uma página que é carregada por ajax, depois em outra guia e assim por diante. clicar no botão Voltar o levará de volta a uma página diferente, não à guia anterior. eles querem percorrer o "histórico do ajax" dentro do "histórico da página". pelo menos essa é minha impressão do que o Yahoo Browser History Manager deve fazer. eu estava atrás de algo um pouco mais básico.
— Tom

A resposta aceita apresenta seu truque do iframe.
— innaM

Respostas:


79

Use um formulário oculto. Os dados do formulário são preservados (normalmente) nos navegadores quando você recarrega ou clica no botão Voltar para retornar a uma página. O seguinte vai para sua página (provavelmente perto do final):

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

No seu javascript, você precisará do seguinte:

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

O js que fareja o formulário deve ser executado depois que o html é totalmente analisado, mas você pode colocar o formulário e o js embutidos no topo da página (segundo js) se a latência do usuário for uma preocupação séria.


5
"Os dados do formulário são preservados (normalmente) nos navegadores" - você pode expandir isso? não é preservado em alguns navegadores? ou em algumas situações?
— Tom,

4
Na verdade, deixe-me listar todas as maneiras pelas quais posso pensar que isso pode dar errado. (1) O Opera mantém o estado completo da página e não executa novamente o js. (2) Se suas configurações de cache forem ajustadas para forçar o carregamento da página do servidor no botão Voltar, os dados do formulário podem ser perdidos, o que é bom para os propósitos desta questão. (3) Os navegadores do telefone têm um cache muito menor e o navegador pode já ter retirado a página do cache (o que não é um problema para esses propósitos).
— Nick White,

10
Em alguns navegadores, as tags ocultas não são preservadas. Portanto, você pode usar a tag de texto oculta em vez de usar a tag de entrada oculta. <input type = "text" value = "0" id = 'txt' name = 'txt' style = "display: none" />
— sajith

2
Isso funcionou muito bem para mim. Tenho certeza de que há situações em que ele quebra, mas todas as soluções resolverão esse problema.
— Joren,

Ele não está funcionando no iPhone com o navegador Safari, experimente, quando você clicar no botão Voltar, js não será chamado novamente.
— woheras

52

Aqui está uma solução moderna muito fácil para esse velho 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 é atualmente suportado por todos os principais navegadores.


Eu não tinha ouvido falar disso. Acabei de encontrar esta documentação ( developer.mozilla.org/en-US/docs/Web/API/Performance/navigation ) e gostaria de ouvi-lo elaborar (por exemplo, por que a necessidade de em window.performance && window.performance.navigation.typevez de apenas window.performance.navigation.TYPE_BACK_FORWARD?).
— Ryan

5
window.performance.navigation.TYPE_BACK_FORWARDé uma constante que é sempre igual a 2. Portanto, é usada apenas para comparação.
— Bassem

Performance.navigation é compatível com todos os dispositivos, exceto andorid e opera mini, mas sem problemas, é muito bom ( developer.mozilla.org/en-US/docs/Web/API/Performance/navigation )
— Elbaz

1
Parece que performance.navigationestá obsoleto e é sugerido usar developer.mozilla.org/en-US/docs/Web/API/…
— RubberDuckRabbit

1
O Firefox v70 oferecerá suporte a performance.navigation = 2para cliques no botão Voltar. Até a v70, ele retornava incorretamente 1.
— Andy Mercer

13

Este artigo explica isso. Veja o código abaixo: 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
Esta é uma boa resposta, funciona no Chrome 40 / Firefox 35 mais recente. No entanto, o IE11 não oferece suporte para esses eventos.
— Slava Abakumov

De acordo com caniuse.com/#feat=page-transition-events , os eventos de transição de página são, na verdade, suportados no IE11 + Como falamos, há 88% de cobertura global do recurso
— Cristian

Para sua informação, o Chrome retorna event.persisted= false, mesmo ao pressionar o botão Voltar. Você deve usar window.performance.navigationou PerformanceNavigationTiming para Chrome.
— Andy Mercer


2

Conforme mencionado acima, eu encontrei uma solução e a estou postando aqui para referência e feedback.

O primeiro estágio da solução é adicionar o seguinte à página:

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

O conteúdo de fresh.htmlnão é importante, então o seguinte deve ser suficiente:

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

Quando o código do lado do cliente atualiza a página, ele precisa sinalizar a modificação da seguinte maneira:

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.htmlfaz todo o trabalho: Quando é carregado, ele chamará a reload_stale_pagefunção que atualizará a página se necessário. Na primeira vez que é carregado (ou seja, após a modificação ser feita, a reload_stale_pagefunção não fará nada.)

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

Pelo meu teste (mínimo) neste estágio, isso parece funcionar como desejado. Eu esqueci alguma coisa?


Não que eu saiba, mas não testei totalmente. getElementById não é compatível com alguns navegadores mais antigos, mas é fácil trocar por jquery ou algo assim, se necessário.
— Tom

2

Você pode resolvê-lo usando o evento onbeforeunload :

window.onbeforeunload = function () { }

Ter uma função de gerenciador de eventos vazio onbeforeunload significa que a página será reconstruída toda vez que for acessada. Javascripts serão executados novamente, scripts do lado do servidor serão executados novamente, a página será construída como se o usuário estivesse acessando-a pela primeira vez, mesmo se o usuário acessou a página apenas pressionando o botão Voltar ou Avançar .

Aqui está o código completo de um exemplo:

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

Experimente, navegue para fora desta página e volte usando os botões "Voltar" ou "Avançar" em seu navegador.

Funciona bem no IE, FF e Chrome.

Claro que você pode fazer o que quiser dentro da função myfun .

Mais informações: http://www.hunlock.com/blogs/Mastering_The_Back_Button_With_Javascript


Funciona no Safari para mim
— Jakob

2

Você pode usar localStorage ou sessionStorage ( http://www.w3schools.com/html/html5_webstorage.asp ) para definir um sinalizador (em vez de usar um formulário oculto).


é uma boa solução alternativa, mas como você faria se ele quisesse detectar isso todas as vezes, quando eu abri e fechei o site, preciso detectar apenas quando abri e voltar antes de fechar
— Elbaz

1

Usando esta página, especialmente incorporando o comentário de @sajith sobre a resposta de @Nick White e esta página: 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

Aqui está uma versão do jQuery. Já tive a necessidade de usá-lo algumas vezes devido à maneira como o Safari para desktop / celular lida com o cache quando um usuário pressiona o botão Voltar.

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

Não parece funcionar no Chrome, tanto quanto posso dizer. Um console.log só é acionado na instrução else (adicionada para teste), independentemente de como eu acesso a página.
— Lucas

0

Eu encontrei um problema semelhante hoje e resolvi com localStorage (aqui com um pouco de 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 basicamente:

Na página 1, quando o usuário envia o formulário, definimos um valor "etapas" em localStorage para indicar a etapa que o usuário realizou. Ao mesmo tempo, lançamos uma função com timeout que irá verificar se este valor foi alterado (por exemplo, 10 vezes / segundo).

Na página 2, alteramos imediatamente o referido valor.

Portanto, se o usuário usar o botão Voltar e o navegador restaurar a página 1 no estado exato em que a deixamos, a função checkSteps ainda está em execução e é capaz de detectar que o valor em localStorage foi alterado (e pode tomar a ação apropriada ) Uma vez que essa verificação tenha cumprido seu propósito, não há necessidade de continuar a executá-la, então simplesmente não usamos mais setTimeout.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.