Intercepting call to the back button dans mon application AJAX: Je ne veux pas qu'il fasse quoi que ce soit

j'ai une application AJAX. Un utilisateur clique sur un bouton, et l'affichage de la page modifications. Ils cliquent sur le bouton Précédent, s'attendant à aller à l'état original, mais à la place, ils vont à la page précédente dans leur navigateur.

Comment puis-je intercepter et réattribuer l'Événement du bouton arrière? J'ai regardé dans les bibliothèques comme RSH (que je ne pouvais pas obtenir pour travailler...), et j'ai entendu dire que l'utilisation de la balise hash en quelque sorte aide, mais je ne peux pas faire sens.

65
demandé sur Benjamin 2009-12-04 05:24:35

9 réponses

Ah, le bouton arrière. Vous pouvez imaginer que" back "déclenche un événement JavaScript que vous pouvez simplement annuler comme ceci:

document.onHistoryGo = function() { return false; }

Non. Il n'ya tout simplement pas de tels cas.

si vous avez vraiment une application web (par opposition à un site Web avec quelques fonctionnalités ajaxy), il est raisonnable de reprendre le bouton précédent (avec des fragments sur L'URL, comme vous le mentionnez). Gmail fait ça. Je parle de quand votre application web dans tous dans une page, tous les auto-contenue.

la technique est simple - chaque fois que l'utilisateur prend des mesures qui modifient les choses, redirigez vers la même URL que vous êtes déjà sur, mais avec un fragment de hachage différent. Par exemple:

window.location.hash = "#deleted_something";
...
window.location.hash = "#did_something_else";

si l'état général de votre application web est hachable, c'est un excellent endroit pour utiliser un hachage. Supposons que vous ayez une liste d'e-mails, peut-être que vous concaténez toutes leurs cartes D'identité et que vous lisiez/n'ayez pas lu les statuts, et que vous prenez un hachage MD5, en l'utilisant comme fragment identificateur.

ce type de redirection (hachage seulement) n'essaie pas de récupérer quoi que ce soit du serveur, mais il insère un slot dans la liste d'historique du navigateur. Dans l'exemple ci-dessus, l'utilisateur clique sur "back" et affiche maintenant #deleted_something dans la barre d'adresse. Ils ont encore frappé et ils sont toujours sur votre page mais sans hash. Puis de nouveau et ils réellement retourner, à l'endroit où ils sont venus.

maintenant la partie difficile cependant, avoir votre JavaScript détecter quand l'Utilisateur a frappé en arrière (de sorte que vous pouvez revenir en arrière l'état). Tout ce que vous faites est de regarder l'emplacement de la fenêtre et de voir quand il change. Avec des bureaux de vote. (Je sais, beurk, les bureaux de vote. Eh bien, il n'y a rien de mieux que de croiser le navigateur pour le moment). Vous ne serez pas en mesure de dire s'ils sont allés de l'avant ou en arrière, donc vous devrez être créatif avec vos identificateurs de hachage. (Peut-être un hachage concaténé avec un numéro de séquence...)

C'est l'essentiel du code. (C'est aussi comme ça que fonctionne le plugin jQuery History.)

var hash = window.location.hash;
setInterval(function(){
    if (window.location.hash != hash) {
        hash = window.location.hash;
        alert("User went back or forward to application state represented by " + hash);
    }
}, 100);
110
répondu jpsimons 2009-12-04 02:59:24

Pour donner un up-to-date de la réponse à un vieux (mais populaire) question:

HTML5 introduit les méthodes history.pushState() et history.replaceState() , qui vous permettent d'ajouter et de modifier des entrées historiques, respectivement. Ces méthodes fonctionnent en conjonction avec l'événement window.onpopstate .

en utilisant history.pushState() change le referrer qui est utilisé dans L'en-tête HTTP pour les objets XMLHttpRequest créés après que vous ayez changé l'état. Le référent sera être L'URL du document dont la fenêtre est this au moment de la création de l'objet XMLHttpRequest .

Source: la Manipulation de l'historique du navigateur de Mozilla Developer Network.

39
répondu gitaarik 2014-12-08 15:52:14

en utilisant jQuery j'ai fait une solution simple:

$(window).bind('hashchange', function() {
    top.location = '#main';
    // Eventually alert the user describing what happened
});

jusqu'à présent seulement testé dans Google Chrome si.

cela a résolu le problème pour mon application web qui est également très AJAX-basé.

il est peut - être un peu hack'ish-mais je l'appellerais hacking élégant; -) chaque fois que vous essayez de naviguer en arrière, il Pop une partie de hachage dans L'URI qui est techniquement ce qu'il essaie ensuite de naviguer en arrière sur.

il intercepte à la fois en cliquant sur le bouton du navigateur et le bouton de la souris. Et vous ne pouvez pas le forcer en arrière soit en cliquant plusieurs fois par seconde, ce qui est un problème qui se produirait dans les solutions basées sur setTimeout ou setInterval.

10
répondu Mark Hünermund Jensen 2017-10-18 09:37:31

j'apprécie vraiment l'explication donnée dans réponse de darkporter , mais je pense qu'il peut être amélioré en utilisant un hashchange " événement . Comme darkporter a expliqué, vous voulez vous assurer que tous vos boutons changent la fenêtre.emplacement.la valeur de hachage.

  • L'un des moyens consiste à utiliser des éléments <button> , puis à y attacher des événements qui établissent ensuite window.location.hash = "#!somehashstring"; .
  • une autre façon de faire cela est d'utiliser des liens pour vos boutons, comme <a href="#!somehashstring">Button 1</a> . Le hachage est automatiquement mis à jour lorsque ces liens sont cliqués.

la raison pour laquelle j'ai un point d'exclamation après le signe de hachage est de satisfaire le paradigme "hashbang" de Google ( en savoir plus sur ce ), qui est utile si vous voulez être indexé par les moteurs de recherche. Votre hashstring sera typiquement des paires nom / valeur comme #!color=blue&shape=triangle ou une liste comme #!/blue/triangle -- tout ce qui a du sens pour votre application web.

alors vous n'avez qu'à ajouter ce bit de code qui s'allume chaque fois que la valeur de hachage change (y compris lorsque le bouton de retour est appuyé ). Aucune boucle de sondage ne semble être nécessaire.

window.addEventListener("hashchange", function(){
    console.log("Hash changed to", window.location.hash);
    // .... Do your thing here...
});

Je n'ai testé que Chrome 36, mais selon caniuse.com , disponible en IE8+, FF21+, Chrome21+, et la plupart des autres navigateurs sauf Opera Mini.

8
répondu Luke 2017-05-23 11:54:37

il est très facile de désactiver le bouton de retour de navigateur , comme le code JQuery ci-dessous:

// History API 
if( window.history && window.history.pushState ){

  history.pushState( "nohb", null, "" );
  $(window).on( "popstate", function(event){
    if( !event.originalEvent.state ){
      history.pushState( "nohb", null, "" );
      return;
    }
  });
}

vous pouvez le voir fonctionner et plus d'exemples ici dotnsf et ici thecssninja

Merci !

5
répondu Roberval Sena 山本 2017-10-19 00:08:30

en raison de préoccupations de confidentialité, il est impossible de désactiver le bouton arrière ou examiner l'histoire de l'utilisateur, mais il est possible de créer de nouvelles entrées dans cette histoire sans changer les pages. Chaque fois que votre application AJAX change d'état, mettez à jour top.location avec un nouveau fragment D'URI .

top.location = "#new-application-state";

cela créera une nouvelle entrée dans la pile historique du navigateur. Un certain nombre de bibliothèques AJAX gèrent déjà tous les détails ennuyeux, tels que Really Simple History .

3
répondu Matthew 2009-12-04 02:44:01

dans ma situation, je voulais empêcher le bouton arrière d'envoyer l'utilisateur à la dernière page et à la place, prendre une autre mesure. En utilisant l'événement hashchange j'ai trouvé une solution qui a fonctionné pour moi. Ce script suppose que la page sur laquelle vous l'utilisez n'utilise pas déjà de hachage, comme dans mon cas:

var hashChangedCount = -2;
$(window).on("hashchange", function () {
    hashChangedCount++;
    if (top.location.hash == "#main" && hashChangedCount > 0) {
        console.log("Ah ah ah! You didn't say the magic word!");
    }
    top.location.hash = '#main';
});
top.location.hash = "#";

le problème que j'avais avec certaines des autres réponses est que l'événement hashchange ne fonctionnerait pas. Selon votre situation, cela peut travaille pour toi aussi.

2
répondu Swisher Sweet 2015-08-30 17:55:07

j'ai trouvé un moyen de modifier le comportement normal de l'historique et de créer des évènements distincts avec les boutons back et forward , en utilisant l'API d'historique HTML5 (cela ne fonctionnera pas dans IE 9). C'est très hacky, mais efficace si vous vouliez intercepter les événements de bouton arrière et avant et les manipuler comme vous voulez. Cela pourrait être utile dans un certain nombre de scénarios, par exemple si vous affichiez une fenêtre de bureau à distance et que vous deviez reproduire des clics de bouton en arrière et en avant sur la machine à distance.

ce qui suit annulerait le comportement des boutons "back" et "forward":

var myHistoryOverride = new HistoryButtonOverride(function()
{
    console.log("Back Button Pressed");
    return true;
},
function()
{
    console.log("Forward Button Pressed");
    return true;
});

si vous avez retourné false dans l'une ou l'autre de ces fonctions de rappel, vous autoriseriez le navigateur à procéder à une opération de retour/avant normale et à quitter votre page.

voici le script complet requis:

function HistoryButtonOverride(BackButtonPressed, ForwardButtonPressed)
{
    var Reset = function ()
    {
        if (history.state == null)
            return;
        if (history.state.customHistoryStage == 1)
            history.forward();
        else if (history.state.customHistoryStage == 3)
            history.back();
    }
    var BuildURLWithHash = function ()
    {
        // The URLs of our 3 history states must have hash strings in them so that back and forward events never cause a page reload.
        return location.origin + location.pathname + location.search + (location.hash && location.hash.length > 1 ? location.hash : "#");
    }
    if (history.state == null)
    {
        // This is the first page load. Inject new history states to help identify back/forward button presses.
        var initialHistoryLength = history.length;
        history.replaceState({ customHistoryStage: 1, initialHistoryLength: initialHistoryLength }, "", BuildURLWithHash());
        history.pushState({ customHistoryStage: 2, initialHistoryLength: initialHistoryLength }, "", BuildURLWithHash());
        history.pushState({ customHistoryStage: 3, initialHistoryLength: initialHistoryLength }, "", BuildURLWithHash());
        history.back();
    }
    else if (history.state.customHistoryStage == 1)
        history.forward();
    else if (history.state.customHistoryStage == 3)
        history.back();

    $(window).bind("popstate", function ()
    {
        // Called when history navigation occurs.
        if (history.state == null)
            return;
        if (history.state.customHistoryStage == 1)
        {
            if (typeof BackButtonPressed == "function" && BackButtonPressed())
            {
                Reset();
                return;
            }
            if (history.state.initialHistoryLength > 1)
                history.back(); // There is back-history to go to.
            else
                history.forward(); // No back-history to go to, so undo the back operation.
        }
        else if (history.state.customHistoryStage == 3)
        {
            if (typeof ForwardButtonPressed == "function" && ForwardButtonPressed())
            {
                Reset();
                return;
            }
            if (history.length > history.state.initialHistoryLength + 2)
                history.forward(); // There is forward-history to go to.
            else
                history.back(); // No forward-history to go to, so undo the forward operation.
        }
    });
};

cela fonctionne par un concept simple. Lorsque notre page se charge, nous créons 3 états d'histoire distincts (numérotés 1, 2 et 3) et naviguez dans le navigateur pour indiquer le numéro 2. Puisque l'état 2 est au milieu, le prochain événement de navigation historique nous placera dans l'état 1 ou 3, et à partir de cela nous pouvons déterminer quelle direction l'Utilisateur a pressée. Et juste comme ça, nous avons intercepté un bouton précédent ou suivant l'événement. Nous le traitons alors comme nous le voulons et nous retournons à l'État numéro 2 afin que nous puissions capturer le prochain événement de navigation historique.

évidemment, vous devez vous abstenir de l'aide de l'histoire.remplaçestate et histoire.pushState méthodes tout en utilisant le script HistoryButtonOverride, sinon vous le briser.

1
répondu Brian 2017-07-06 15:59:38

je fais quelque chose comme ça. Je garde un tableau avec application précédente unis.

initié par:

var backstack = [];

alors j'écoute les changements dans le hachage de localisation, et quand il change je fais ceci:

if (backstack.length > 1 && backstack[backstack.length - 2] == newHash) {
    // Back button was probably pressed
    backstack.pop();
} else
    backstack.push(newHash);

de Cette façon, j'ai un peu moyen simple de garder une trace de l'utilisateur de l'histoire. Maintenant, si je veux implémenter un bouton back dans l'application (pas le navigateur-botton), je le fais juste faire:

window.location.hash = backstack.pop();
0
répondu Trenskow 2012-10-08 16:29:24