web-dev-qa-db-fra.com

JS - window.history - Efface un état

En utilisant l’API html5 window.history, Je peux assez bien contrôler la navigation sur mon application Web.

L'application a actuellement deux états: selectDate (1) et enterDetails (2).

Lorsque l'application est chargée, je replaceState et je définis un auditeur popState:

history.replaceState({stage:"selectDate",...},...);
window.onpopstate = function(event) {
    that.toStage(event.state.stage);
};

Lorsqu'une date est sélectionnée et que l'application passe à l'étape 2, j'appuie l'état 2 sur la pile:

history.pushState({stage:"enterDetails",...},...);

Cet état est remplacé chaque fois que les détails changent afin qu'ils soient sauvegardés dans l'historique.

Il y a trois façons de quitter l'étape 2:

  • enregistrer (soumettre ajax)
  • annuler
  • bouton retour

Le bouton Précédent est géré par le listener popstate. Le bouton d'annulation pousse l'étape 1 afin que l'utilisateur puisse revenir aux détails qu'il avait saisis dans le bouton retour. Ces deux fonctionnent bien.

Le bouton de sauvegarde doit revenir à l'étape 1 et ne pas permettre à l'utilisateur de revenir à la page des détails (car ils ont déjà soumis). Fondamentalement, y devrait faire en sorte que la pile d’historique soit length = 1.

Mais il ne semble pas y avoir de history.delete(), ni de history.merge(). Le mieux que je puisse faire est une history.replaceState(stage1) qui laisse la pile d'historique sous la forme: ["selectDate","selectDate"].

Comment puis-je me débarrasser d'une couche?

Edit:

Pensé à autre chose, mais ça ne marche pas non plus.

history.back(); //moves history to the correct position
location.href = "#foo"; // successfully removes ability to go 'forward', 
                       // but also adds another layer to the history stack

Cela laisse la pile d'historique sous la forme ["selectDate","selectDate#foo"].

Alors, au lieu de cela, y a-t-il un moyen de supprimer l'historique 'en avant' sans pousser un nouvel état?

43
slicedtoad

Vous êtes peut-être passé maintenant, mais ... autant que je sache, il n'y a aucun moyen de supprimer une entrée de l'historique (ou un état).

L’une des options que j’ai étudiée est de gérer l’historique vous-même en JavaScript et d’utiliser le window.history objet en tant que transporteur.

Fondamentalement, lorsque la page se charge pour la première fois, vous créez votre objet d'historique personnalisé (nous allons utiliser un tableau ici, mais utilisez ce qui convient à votre situation), faites ensuite votre pushState initiale. Je transmettrais votre objet d'historique personnalisé en tant qu'objet d'état, car il peut s'avérer utile si vous devez également gérer les utilisateurs quittant votre application pour revenir plus tard.

var myHistory = [];

function pageLoad() {
    window.history.pushState(myHistory, "<name>", "<url>");

    //Load page data.
}

Maintenant, lorsque vous naviguez, vous ajoutez à votre propre objet d’historique (ou pas - l’historique est maintenant entre vos mains!) Et vous utilisez replaceState pour que le navigateur ne se trouve pas dans la boucle.

function nav_to_details() {
    myHistory.Push("page_im_on_now");
    window.history.replaceState(myHistory, "<name>", "<url>");

    //Load page data.
}

Lorsque l'utilisateur reviendra en arrière, il atteindra votre état "base" (votre objet state sera null) et vous pourrez gérer la navigation en fonction de votre objet d'historique personnalisé. Ensuite, vous faites un autre pushState.

function on_popState() {
    // Note that some browsers fire popState on initial load,
    // so you should check your state object and handle things accordingly.
    // (I did not do that in these examples!)

    if (myHistory.length > 0) {
        var pg = myHistory.pop();
        window.history.pushState(myHistory, "<name>", "<url>");

        //Load page data for "pg".
    } else {
        //No "history" - let them exit or keep them in the app.
    }
}

L'utilisateur ne pourra jamais naviguer en avant à l'aide des boutons de son navigateur, car ils sont toujours sur la page la plus récente.

Du point de vue du navigateur, chaque fois qu’ils reviennent "en arrière", ils ont immédiatement repoussé.

Du point de vue de l'utilisateur, il peut naviguer dans les pages en arrière mais pas en avant (en simulant le modèle de pile de pages du smartphone).

Du point de vue du développeur, vous avez maintenant un haut niveau de contrôle sur la manière dont l'utilisateur navigue dans votre application, tout en lui permettant d'utiliser les boutons de navigation familiers de son navigateur. Vous pouvez ajouter/supprimer des éléments n'importe où dans la chaîne de l'historique à votre guise. Si vous utilisez des objets dans votre tableau d'historique, vous pouvez également suivre des informations supplémentaires sur les pages (contenu du champ, etc.).

Si vous devez gérer la navigation initiée par l'utilisateur (par exemple, si l'utilisateur modifie l'URL dans un schéma de navigation basé sur un hachage), vous pouvez utiliser une approche légèrement différente, comme ...

var myHistory = [];

function pageLoad() {
    // When the user first hits your page...
    // Check the state to see what's going on.

    if (window.history.state === null) {
        // If the state is null, this is a NEW navigation,
        //    the user has navigated to your page directly (not using back/forward).

        // First we establish a "back" page to catch backward navigation.
        window.history.replaceState(
            { isBackPage: true },
            "<back>",
            "<back>"
        );

        // Then Push an "app" page on top of that - this is where the user will sit.
        // (As browsers vary, it might be safer to put this in a short setTimeout).
        window.history.pushState(
            { isBackPage: false },
            "<name>",
            "<url>"
        );

        // We also need to start our history tracking.
        myHistory.Push("<whatever>");

        return;
    }

    // If the state is NOT null, then the user is returning to our app via history navigation.

    // (Load up the page based on the last entry of myHistory here)

    if (window.history.state.isBackPage) {
        // If the user came into our app via the back page,
        //     you can either Push them forward one more step or just use pushState as above.

        window.history.go(1);
        // or window.history.pushState({ isBackPage: false }, "<name>", "<url>");
    }

    setTimeout(function() {
        // Add our popstate event listener - doing it here should remove
        //     the issue of dealing with the browser firing it on initial page load.
        window.addEventListener("popstate", on_popstate);
    }, 100);
}

function on_popstate(e) {
    if (e.state === null) {
        // If there's no state at all, then the user must have navigated to a new hash.

        // <Look at what they've done, maybe by reading the hash from the URL>
        // <Change/load the new page and Push it onto the myHistory stack>
        // <Alternatively, ignore their navigation attempt by NOT loading anything new or adding to myHistory>

        // Undo what they've done (as far as navigation) by kicking them backwards to the "app" page
        window.history.go(-1);

        // Optionally, you can throw another replaceState in here, e.g. if you want to change the visible URL.
        // This would also prevent them from using the "forward" button to return to the new hash.
        window.history.replaceState(
            { isBackPage: false },
            "<new name>",
            "<new url>"
        );
    } else {
        if (e.state.isBackPage) {
            // If there is state and it's the 'back' page...

            if (myHistory.length > 0) {
                // Pull/load the page from our custom history...
                var pg = myHistory.pop();
                // <load/render/whatever>

                // And Push them to our "app" page again
                window.history.pushState(
                    { isBackPage: false },
                    "<name>",
                    "<url>"
                );
            } else {
                // No more history - let them exit or keep them in the app.
            }
        }

        // Implied 'else' here - if there is state and it's NOT the 'back' page
        //     then we can ignore it since we're already on the page we want.
        //     (This is the case when we Push the user back with window.history.go(-1) above)
    }
}
54
Mike B.

Il n'y a aucun moyen de supprimer ou de lire l'historique.

Vous pouvez essayer de le contourner en imitant l’histoire dans votre propre mémoire et en appelant history.pushState chaque fenêtre popstate événement est émis (ce qui est proposé par la réponse de Mike actuellement acceptée ), mais il présente de nombreux inconvénients qui aboutiront à une pire expérience UX que de ne pas supporter le historique du navigateur dans votre application Web dynamique, car:

  • un événement popstate peut se produire lorsque l'utilisateur revient ~ 2-3 états au passé
  • un événement popstate peut se produire lorsque l'utilisateur avance

Donc, même si vous essayez de contourner le problème en construisant un historique virtuel, il est très probable que cela puisse également conduire à une situation dans laquelle vous avez des états vierges (auxquels aucun retour/retour ne sert à rien) ou de votre histoire déclare totalement.

1
jtompl