web-dev-qa-db-fra.com

Comment détecter si un utilisateur a accédé à une page à l'aide du bouton de retour?

Cette question est similaire à suivre lorsque l'utilisateur appuie sur le bouton de retour sur le navigateur , mais pas la même ... J'ai une solution et je la poste ici pour référence et commentaires. Si quelqu'un a de meilleures options, je suis à l'écoute!

La situation est que j'ai une page avec un "éditer sur place", à la flickr. C'est à dire. il y a un DIV "cliquez ici pour ajouter une description" qui, lorsque vous cliquez dessus, se transforme en TEXTAREA avec les boutons Enregistrer et Annuler. Un clic sur Enregistrer publie les données sur le serveur pour mettre à jour la base de données et place la nouvelle description dans le DIV à la place du TEXTAREA. Si la page est actualisée, la nouvelle description est affichée à partir de la base de données avec une option "cliquez pour modifier". Des trucs web 2.0 assez standard de nos jours.

Le problème est que si:

  1. la page est chargée sans la description
  2. une description est ajoutée par l'utilisateur
  3. la page est éloignée en cliquant sur un lien
  4. l'utilisateur clique sur le bouton de retour

Ensuite, ce qui est affiché (à partir du cache du navigateur) est la version de la page sans le DIV modifié dynamiquement contenant la nouvelle description.

Il s'agit d'un problème assez important car l'utilisateur suppose que sa mise à jour a été perdue et ne comprendra pas nécessairement qu'il doit actualiser la page pour voir les modifications.

Ainsi, la question est: comment pouvez-vous marquer une page comme étant modifiée après son chargement, puis détecter quand l'utilisateur "y revient" et forcer un rafraîchissement dans cette situation?

86
Tom

Utilisez un formulaire caché. Les données de formulaire sont préservées (généralement) dans les navigateurs lorsque vous rechargez ou appuyez sur le bouton de retour pour revenir à une page. Ce qui suit va dans votre page (probablement vers le bas):

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

Dans votre javascript, vous aurez besoin des éléments suivants:

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

Le js qui renifle le formulaire doit s'exécuter après que le html est entièrement analysé, mais vous pouvez mettre le formulaire et le js en ligne en haut de la page (js seconde) si la latence de l'utilisateur est un problème grave.

77
Nick White

Voici une solution moderne très simple à ce vieux problème.

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 est actuellement pris en charge par tous les principaux navigateurs.

44
Bassem

Cet article l'explique. Voir le code ci-dessous: 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>
13
Drew Baker

Vous pouvez le résoudre en utilisant l'événement onbeforeunload :

window.onbeforeunload = function () { }

Le fait d'avoir une fonction de gestionnaire d'événements vide onbeforeunload signifie que la page sera reconstruite à chaque accès. Les javascripts seront réexécutés, les scripts côté serveur seront réexécutés, la page sera construite comme si l'utilisateur la touchait pour la première fois, même si l'utilisateur accédait à la page simplement en appuyant sur le bouton Précédent ou Suivant. .

Voici le code complet d'un exemple:

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

Essayez-le, naviguez sur cette page, puis revenez en utilisant les boutons "Précédent" ou "Suivant" de votre navigateur.

Cela fonctionne bien dans IE, FF et Chrome.

Bien sûr, vous pouvez faire ce que vous voulez dans la fonction myfun .

Plus d'informations: http://www.hunlock.com/blogs/Mastering_The_Back_Button_With_Javascript

2
Nes

Vous pouvez utiliser localStorage ou sessionStorage ( http://www.w3schools.com/html/html5_webstorage.asp ) pour définir un indicateur (au lieu d'utiliser un formulaire masqué).

2
rerezz

Comme mentionné ci-dessus, j'avais trouvé une solution et je la poste ici pour référence et commentaires.

La première étape de la solution consiste à ajouter ce qui suit à la page:

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

Le contenu de fresh.html ne sont pas importants, donc ce qui suit devrait suffire:

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

Lorsque le code côté client met à jour la page, il doit marquer la modification comme suit:

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 fait tout le travail: une fois chargé, il appellera reload_stale_page fonction qui rafraîchira la page si nécessaire. La première fois qu'il est chargé (c'est-à-dire après la modification, le reload_stale_page la fonction ne fera rien.)

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

D'après mes tests (minimes) à ce stade, cela semble fonctionner comme souhaité. Ai-je oublié quelque chose?

2
Tom

Utilisation de cette page, notamment en intégrant le commentaire de @sajith sur la réponse de @Nick White et cette page: 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
nmit026

Voici une version jQuery. J'ai eu besoin de l'utiliser à plusieurs reprises en raison de la façon dont Safari Desktop/Mobile gère le cache lorsqu'un utilisateur appuie sur le bouton Retour.

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

J'ai rencontré un problème similaire aujourd'hui et je l'ai résolu avec localStorage (ici avec un peu 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 fondamentalement:

Sur la page 1, lorsque l'utilisateur soumet le formulaire, nous définissons une valeur "étapes" dans localStorage pour indiquer quelle étape l'utilisateur a prise. Dans le même temps, nous lançons une fonction avec timeout qui vérifiera si cette valeur a été modifiée (par exemple 10 fois/seconde).

À la page 2, nous modifions immédiatement ladite valeur.

Donc, si l'utilisateur utilise le bouton de retour et que le navigateur restaure la page 1 dans l'état exact où elle était lorsque nous l'avons laissée, la fonction checkSteps est toujours en cours d'exécution et capable de détecter que la valeur dans localStorage a été modifiée (et peut prendre les mesures appropriées ). Une fois que cette vérification a rempli son objectif, il n'est plus nécessaire de continuer à l'exécuter, nous n'utilisons donc tout simplement plus setTimeout.

0
s427