web-dev-qa-db-fra.com

Comment détecter un événement du bouton Précédent du navigateur - Navigateur croisé

Comment détecter définitivement si l'utilisateur a ou non appuyé sur le bouton Retour du navigateur? 

Comment imposer l'utilisation d'un bouton de retour dans la page au sein d'une application Web d'une page utilisant un système #URL

Pourquoi diable les boutons de navigateur de retour ne déclenchent-ils pas leurs propres événements!?

147
Xarus

(Remarque: selon les commentaires de Sharky, j'ai inclus du code pour détecter les espaces de retour)

Donc, j'ai souvent vu ces questions sur SO, et j'ai récemment rencontré le problème du contrôle de la fonctionnalité du bouton de retour moi-même. Après quelques jours de recherche de la solution la mieux adaptée à mon application (page unique avec navigation par hachage), j'ai mis au point un système simple, multi-navigateur et sans bibliothèque, permettant de détecter le bouton Précédent.

La plupart des gens recommandent d'utiliser:

window.onhashchange = function() {
 //blah blah blah
}

Cependant, cette fonction sera également appelée lorsqu'un utilisateur utilise un élément de la page qui modifie le hachage de l'emplacement. Ce n'est pas la meilleure expérience utilisateur lorsque votre utilisateur clique et que la page avance ou recule.

Pour vous donner un aperçu général de mon système, je remplis un tableau avec les hachages précédents au fur et à mesure que mon utilisateur se déplace dans l'interface. Cela ressemble à quelque chose comme ça:

function updateHistory(curr) {
    window.location.lasthash.Push(window.location.hash);
    window.location.hash = curr;
}

Assez simple. Je le fais pour assurer la prise en charge multi-navigateurs, ainsi que pour les navigateurs plus anciens. Il vous suffit de transmettre le nouveau hachage à la fonction, qui le stockera pour vous, puis modifiera le hachage (qui sera ensuite placé dans l'historique du navigateur).

J'utilise également un bouton de retour dans la page qui déplace l'utilisateur entre les pages à l'aide du tableau lasthash. Cela ressemble à ceci:

function goBack() {
    window.location.hash = window.location.lasthash[window.location.lasthash.length-1];
    //blah blah blah
    window.location.lasthash.pop();
}

Donc, cela ramènera l'utilisateur au dernier hachage et supprimera ce dernier du tableau (je n'ai pas de bouton de transfert pour le moment).

Alors. Comment détecter si un utilisateur a utilisé ou non mon bouton Précédent de la page ou le bouton du navigateur?

Au début, j’ai regardé window.onbeforeunload, mais en vain - cela n’est appelé que si l’utilisateur change de page. Cela ne se produit pas dans une application simple page utilisant la navigation par hachage.

Ainsi, après quelques recherches supplémentaires, j'ai vu des recommandations pour essayer de définir une variable de drapeau. Le problème avec ceci dans mon cas, c’est que j’essayerais de le définir, mais comme tout est asynchrone, il ne sera pas toujours réglé à temps pour l’instruction if dans le changement de hachage. .onMouseDown n'a pas toujours été appelé en un clic, et l'ajouter à un onclick ne le déclencherait jamais assez rapidement.

C'est à ce moment que j'ai commencé à regarder la différence entre document et window. Ma dernière solution consistait à définir l'indicateur à l'aide de document.onmouseover et à le désactiver à l'aide de document.onmouseleave.

En effet, lorsque la souris de l'utilisateur se trouve dans la zone du document (à lire: la page rendue, à l'exclusion du cadre du navigateur), mon booléen est défini sur true. Dès que la souris quitte la zone du document, le booléen bascule à false.

De cette façon, je peux changer mon window.onhashchange en:

window.onhashchange = function() {
    if (window.innerDocClick) {
        window.innerDocClick = false;
    } else {
        if (window.location.hash != '#undefined') {
            goBack();
        } else {
            history.pushState("", document.title, window.location.pathname);
            location.reload();
        }
    }
}

Vous noterez le chèque pour #undefined. En effet, s'il n'y a pas d'historique disponible dans mon tableau, il retourne undefined. J'utilise ceci pour demander à l'utilisateur s'il souhaite partir en utilisant un événement window.onbeforeunload.

En bref, et pour les personnes qui n'utilisent pas nécessairement un bouton de retour dans la page ou un tableau pour stocker l'historique:

document.onmouseover = function() {
    //User's mouse is inside the page.
    window.innerDocClick = true;
}

document.onmouseleave = function() {
    //User's mouse has left the page.
    window.innerDocClick = false;
}

window.onhashchange = function() {
    if (window.innerDocClick) {
        //Your own in-page mechanism triggered the hash change
    } else {
        //Browser back button was clicked
    }
}

Et voila. un moyen simple en trois parties de détecter l’utilisation du bouton Précédent par rapport aux éléments de la page concernant la navigation par hachage.

MODIFIER:

Pour vous assurer que l'utilisateur n'utilise pas le retour arrière pour déclencher l'événement back, vous pouvez également inclure les éléments suivants (Merci à @thetoolman sur this Question ):

$(function(){
    /*
     * this swallows backspace keys on any non-input element.
     * stops backspace -> back
     */
    var rx = /INPUT|SELECT|TEXTAREA/i;

    $(document).bind("keydown keypress", function(e){
        if( e.which == 8 ){ // 8 == backspace
            if(!rx.test(e.target.tagName) || e.target.disabled || e.target.readOnly ){
                e.preventDefault();
            }
        }
    });
});
134
Xarus

Vous pouvez essayer popstate gestionnaire d’événements, par exemple:

window.addEventListener('popstate', function(event) {
    // The popstate event is fired each time when the current history entry changes.

    var r = confirm("You pressed a Back button! Are you sure?!");

    if (r == true) {
        // Call Back button programmatically as per user confirmation.
        history.back();
        // Uncomment below line to redirect to the previous page instead.
        // window.location = document.referrer // Note: IE11 is not supporting this.
    } else {
        // Stay on the current page.
        history.pushState(null, null, window.location.pathname);
    }

    history.pushState(null, null, window.location.pathname);

}, false);

Remarque: pour obtenir les meilleurs résultats, vous devez charger ce code uniquement sur des pages spécifiques sur lesquelles vous souhaitez implémenter la logique afin d'éviter tout autre problème imprévu.

L'événement popstate est déclenché chaque fois que l'entrée d'historique actuelle est modifiée (l'utilisateur accède à un nouvel état). Cela se produit lorsque l'utilisateur clique sur les boutons Précédent/Suivant du navigateur ou lorsque les méthodes history.back(), history.forward(), history.go() sont appelées par programme.

La propriété event.state de l'événement est égale à l'objet d'état d'historique.

Pour la syntaxe jQuery, retournez-le (pour ajouter un écouteur même après que le document est prêt):

(function($) {
  // Above code here.
})(jQuery);

Voir aussi: window.onpopstate au chargement de la page


Voir également les exemples sur Applications mono-page et HTML5 pushState page:

<script>
// jQuery
$(window).on('popstate', function (e) {
    var state = e.originalEvent.state;
    if (state !== null) {
        //load content with ajax
    }
});

// Vanilla javascript
window.addEventListener('popstate', function (e) {
    var state = e.state;
    if (state !== null) {
        //load content with ajax
    }
});
</script>

Cela devrait être compatible avec Chrome 5+, Firefox 4+, IE 10+, Safari 6+, Opera 11.5+ et similaires.

53
kenorb

Cela faisait longtemps que je luttais avec cette exigence et j'ai pris certaines des solutions ci-dessus pour la mettre en œuvre. Cependant, je suis tombé sur une observation qui semble fonctionner avec les navigateurs Chrome, Firefox et Safari + Android et iPhone 

Sur le chargement de la page:

window.history.pushState({page: 1}, "", "");

window.onpopstate = function(event) {

  // "event" object seems to contain value only when the back button is clicked
  // and if the pop state event fires due to clicks on a button
  // or a link it comes up as "undefined" 

  if(event){
    // Code to handle back button or prevent from navigation
  }
  else{
    // Continue user action through link or button
  }
}

Faites-moi savoir si cela aide. Si je manque quelque chose, je serai heureux de comprendre.

11
Itzmeygu

En javascript, le type de navigation 2 signifie que le bouton Précédent/Suivant du navigateur a été cliqué et que le navigateur extrait actuellement le contenu du cache.

if(performance.navigation.type == 2)
{
    //Do your code here
}
7
Hasan Badshah

Voici mon point de vue. L'hypothèse est la suivante: lorsque l'URL change mais qu'il n'y a pas de clic dans la variable document détectée, il s'agit d'un navigateur précédent (oui ou suivant). Un clic d'utilisateur est réinitialisé après 2 secondes pour que cela fonctionne sur les pages qui chargent du contenu via Ajax:

(function(window, $) {
  var anyClick, consoleLog, debug, delay;
  delay = function(sec, func) {
    return setTimeout(func, sec * 1000);
  };
  debug = true;
  anyClick = false;
  consoleLog = function(type, message) {
    if (debug) {
      return console[type](message);
    }
  };
  $(window.document).click(function() {
    anyClick = true;
    consoleLog("info", "clicked");
    return delay(2, function() {
      consoleLog("info", "reset click state");
      return anyClick = false;
    });
  });
  return window.addEventListener("popstate", function(e) {
    if (anyClick !== true) {
      consoleLog("info", "Back clicked");
      return window.dataLayer.Push({
        event: 'analyticsEvent',
        eventCategory: 'test',
        eventAction: 'test'
      });
    }
  });
})(window, jQuery);
2
TomTom101

La réponse correcte est déjà là pour répondre à la question. Je veux mentionner la nouvelle API JavaScript PerformanceNavigationTiming , elle remplace obsolète performance.navigation .

Le code suivant ouvrira une session "back_forward" sur la console si l’utilisateur a atterri sur votre page à l’aide du bouton Précédent ou Suivant. Regardez le tableau de compatibilité avant de l'utiliser dans votre projet.

var perfEntries = performance.getEntriesByType("navigation");
for (var i = 0; i < perfEntries.length; i++) {
    console.log(perfEntries[i].type);
}
1
llaaalu

J'ai effectué cette tâche avec l'astuce ci-dessous.Avec cet écouteur d'événement ajouté, appelé lorsque quelqu'un revient à la page avec le bouton Précédent du navigateur et lors de cet événement, il a simplement rechargé la page. J'espère que cela pourra aider.

window.addEventListener("pageshow", function(event) {
  var historyTraversal = event.persisted || (typeof window.performance !=
    "undefined" && window.performance.navigation.type === 2);
  if (historyTraversal) {
    // Handle page restore. 
    window.location.reload();
  }
});

ajouté ce code au bas de la page.

1
mohitesachin217

J'ai pu utiliser certaines des réponses de ce fil et d'autres pour le faire fonctionner dans IE et Chrome/Edge. history.pushState pour moi n'a pas été pris en charge dans IE11.

if (history.pushState) {
    //Chrome and modern browsers
    history.pushState(null, document.title, location.href);
    window.addEventListener('popstate', function (event) {
        history.pushState(null, document.title, location.href);
    });
}
else {
    //IE
    history.forward();
}
1
Arsalan Siddiqui

Le document.mouseover ne fonctionne pas pour IE et FireFox . Cependant, j'ai essayé ceci:

$(document).ready(function () {
  setInterval(function () {
    var $sample = $("body");
    if ($sample.is(":hover")) {
      window.innerDocClick = true;
    } else {
      window.innerDocClick = false;
    }
  });

});

window.onhashchange = function () {
  if (window.innerDocClick) {
    //Your own in-page mechanism triggered the hash change
  } else {
    //Browser back or forward button was pressed
  }
};

Cela fonctionne pour Chrome et IE et non pas pour FireFox. Nous travaillons toujours pour que FireFox fonctionne correctement. Tous les moyens faciles de détecter le clic du bouton de navigation Précédent/Suivant du navigateur sont les bienvenus, pas particulièrement dans JQuery mais également dans AngularJS ou le Javascript pur. 

1
Dhruv Gupta
if (window.performance && window.performance.navigation.type == window.performance.navigation.TYPE_BACK_FORWARD) {
  alert('hello world');
}

C’est la seule solution qui a fonctionné pour moi (ce n’est pas un site Web onepage). Cela fonctionne avec Chrome, Firefox et Safari.

0
escanxr

Je l'ai résolu en gardant une trace de l'événement d'origine qui a déclenché la hashchange (que ce soit un balayage, un clic ou une molette), afin que l'événement ne soit pas confondu avec un simple affichage sur la page, et en utilisant un indicateur supplémentaire dans chacune de mes liaisons d'événement. Le navigateur ne redéfinira pas l'indicateur sur false lorsque vous appuierez sur le bouton Précédent:

var evt = null,
canGoBackToThePast = true;

$('#next-slide').on('click touch', function(e) {
    evt = e;
    canGobackToThePast = false;
    // your logic (remember to set the 'canGoBackToThePast' flag back to 'true' at the end of it)
}
0
user1025495
 <input style="display:none" id="__pageLoaded" value=""/>


 $(document).ready(function () {
        if ($("#__pageLoaded").val() != 1) {

            $("#__pageLoaded").val(1);


        } else {
            shared.isBackLoad = true;
            $("#__pageLoaded").val(1);  

            // Call any function that handles your back event

        }
    });

Le code ci-dessus a fonctionné pour moi. Sur les navigateurs mobiles, lorsque l’utilisateur a cliqué sur le bouton Précédent, nous avons voulu restaurer l’état de la page conformément à sa visite précédente.

0
Ashwin

Cela fonctionnera certainement (pour détecter le clic du bouton de retour)

$(window).on('popstate', function(event) {
 alert("pop");
});
0
rohan parab

Regarde ça:

history.pushState(null, null, location.href);
    window.onpopstate = function () {
        history.go(1);
    };

ça fonctionne bien...

0
Jorge Rocha