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


90

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

La situation est que j'ai une page avec une "édition sur place", à la flickr. C'est-à-dire qu'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. Cliquez sur Enregistrer pour publier les données sur le serveur pour mettre à jour la base de données et placer la nouvelle description dans le DIV à la place de TEXTAREA. Si la page est actualisée, la nouvelle description est affichée à partir de la base de données avec une option «cliquer 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 parcourue en cliquant sur un lien
  4. l'utilisateur clique sur le bouton 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.

C'est 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.

La question est donc la suivante: comment marquer une page comme étant modifiée après son chargement, puis détecter quand l'utilisateur «y retourne» et forcer une actualisation dans cette situation?


En quoi est-ce différent de la question que vous avez citée?
innaM

la question est similaire, mais je pense que l'environnement et donc la réponse est différent, je peux me tromper. mon interprétation de ce que peut être un problème qui serait résolu avec l'autre solution est la suivante: l'utilisateur clique sur un onglet sur une page chargée par ajax, puis sur un autre onglet et ainsi de suite. cliquer sur le bouton de retour vous ramènerait à une page différente, pas à l'onglet précédent. ils veulent revenir en arrière dans «l'historique ajax» dans «l'historique des pages». du moins, c'est mon impression de ce que le gestionnaire de l'historique du navigateur Yahoo est censé faire. J'étais après quelque chose d'un peu plus basique.
Tom

La réponse acceptée présente votre astuce iframe.
innaM

Réponses:


79

Utilisez un formulaire caché. Les données du formulaire sont conservées (généralement) dans les navigateurs lorsque vous rechargez ou appuyez sur le bouton Précédent pour revenir à une page. Ce qui suit apparaît 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 une fois le html entièrement analysé, mais vous pouvez mettre le formulaire et le js en ligne en haut de la page (js en second) si la latence de l'utilisateur est un problème sérieux.


5
«Les données de formulaire sont conservées (généralement) dans les navigateurs» - pouvez-vous développer cela? n'est-il pas conservé dans certains navigateurs? ou dans certaines situations?
Tom

4
En fait, permettez-moi d'énumérer toutes les façons dont je peux penser que cela va mal. (1) Opera conserve l'état complet de la page et ne ré-exécutera pas le js. (2) Si vos paramètres de cache sont modifiés pour forcer le chargement de la page à partir du serveur sur le bouton de retour, les données du formulaire peuvent être perdues, ce qui convient aux besoins de cette question. (3) Les navigateurs du téléphone ont un cache beaucoup plus petit et le navigateur peut déjà avoir supprimé la page du cache (ce n'est pas un problème à ces fins).
Nick White

10
Dans certains navigateurs, les balises masquées ne sont pas conservées. Vous pouvez donc utiliser une balise de texte masquée au lieu d'utiliser une balise d'entrée masquée. <input type = "text" value = "0" id = 'txt' name = 'txt' style = "display: none" />
sajith

2
Cela a très bien fonctionné pour moi. Je suis sûr qu'il y a des situations où il tombe en panne, mais chaque solution le fera pour ce problème.
Joren

Cela ne fonctionne pas sur iPhone avec le navigateur Safari, essayez-le, lorsque vous cliquez sur le bouton retour, js n'est pas appelé à nouveau.
woheras

52

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.


Je n'en avais pas entendu parler. Je viens de trouver cette documentation ( developer.mozilla.org/en-US/docs/Web/API/Performance/navigation ) et serais intéressé de vous entendre élaborer (par exemple, pourquoi le besoin au window.performance && window.performance.navigation.typelieu de juste window.performance.navigation.TYPE_BACK_FORWARD?).
Ryan

5
window.performance.navigation.TYPE_BACK_FORWARDest une constante qui est toujours égale à 2. Elle n'est donc utilisée qu'à des fins de comparaison.
Bassem

Performance.navigation pris en charge sur tous les appareils sauf andorid et opera mini mais pas de problème c'est très agréable ( developer.mozilla.org/en-US/docs/Web/API/Performance/navigation )
Elbaz

1
Il semble que ce performance.navigationsoit obsolète et il est suggéré d'utiliser à la place developer.mozilla.org/en-US/docs/Web/API/...
RubberDuckRabbit

1
Firefox v70 prendra en charge performance.navigation = 2pour les touches de retour. Jusqu'à la v70, il est retourné de manière incorrecte 1.
Andy Mercer

13

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>

1
C'est une bonne réponse, fonctionne dans le dernier Chrome 40 / Firefox 35. Cependant, IE11 ne prend pas en charge ces événements.
Slava Abakumov le

Selon caniuse.com/#feat=page-transition-events, les événements de transition de page sont en fait pris en charge dans IE11 + Au moment où nous parlons, il y a une couverture mondiale de 88% de la fonctionnalité
Cristian

FYI, Chrome renvoie event.persisted= false, même en appuyant sur le bouton de retour. Vous devez utiliser window.performance.navigationou PerformanceNavigationTiming pour Chrome.
Andy Mercer


2

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 les éléments suivants à 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.htmln'est pas important, 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.htmlfait tout le travail: lorsqu'il est chargé, il appellera la reload_stale_pagefonction qui actualisera la page si nécessaire. La première fois qu'elle est chargée (c'est-à-dire qu'après la modification, la reload_stale_pagefonction 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?


Pas que je sache, mais je ne l'ai pas testé complètement. getElementById n'est pas pris en charge dans certains navigateurs plus anciens, mais c'est facile à remplacer par jquery ou quelque chose si nécessaire.
Tom

2

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

window.onbeforeunload = function () { }

Avoir une fonction de gestionnaire d'événements onbeforeunload vide signifie que la page sera recréée à chaque fois qu'elle est accédée. 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 frappait pour la toute 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, quittez cette page, puis revenez en utilisant les boutons «Précédent» ou «Suivant» de votre navigateur.

Cela fonctionne très bien dans IE, FF et Chrome.

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

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


Fonctionne dans Safari pour moi
Jakob

2

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é).


c'est une belle solution de contournement mais comment le faire s'il veut détecter cela à chaque fois, lorsque j'ai ouvert le site et l'ai fermé, je dois détecter juste quand j'ai ouvert et revenir en arrière avant de fermer
Elbaz

1

En utilisant cette page, en intégrant notamment 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

Voici une version de jQuery. J'ai eu besoin de l'utiliser plusieurs fois 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
    }
});

Pour autant que je sache, cela ne semble pas fonctionner dans Chrome. Un console.log n'est déclenché que dans l'instruction else (ajoutée pour le test) quelle que soit la façon dont j'accède à la page.
Luke

0

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. En 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 Précédent et que le navigateur restaure la page 1 dans l'état exact où elle était lorsque nous l'avons quitté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 pas nécessaire de continuer à l'exécuter, nous n'utilisons donc plus setTimeout.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.