Javascript JS - window.history - 删除状态

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28028297/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:07:34  来源:igfitidea点击:

JS - window.history - Delete a state

javascripthtmlbrowser-history

提问by DanielST

Using the html5 window.historyAPI, I can control the navigation pretty well on my web app.

使用 html5 window.historyAPI,我可以很好地控制我的网络应用程序上的导航。

The app currently has two states: selectDate(1) and enterDetails(2).

该应用程序当前有两种状态:selectDate(1) 和enterDetails(2)。

When the app loads, I replaceStateand set a popStatelistener:

当应用加载时,我replaceState设置了一个popState监听器:

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

When a date is selected and the app moves to stage 2 I push state 2 onto the stack:

When a date is selected and the app moves to stage 2 I push state 2 onto the stack:

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

This state is replaced anytime details change so they are saved in the history.

此状态会在详细信息更改时替换,因此它们会保存在历史记录中。

There are three ways to leave stage 2:

离开第 2 阶段的方法有以下三种:

  • save (ajax submit)
  • cancel
  • back button
  • 保存(ajax提交)
  • 取消
  • 返回键

The back button is handled by the popstatelistener. The cancel button pushesstage 1 so that the user can go back to the details they were entering the back button. These both work well.

后退按钮由popstate侦听器处理。取消按钮推动阶段 1,以便用户可以返回到他们输入后退按钮的详细信息。这两个都很好用。

The save button should revert back to stage 1 and not allow the user to navigate back to the details page(since they already submitted). Basical, y it should make the history stack be length = 1.

保存按钮应该恢复到第 1 阶段,并且不允许用户导航回详细信息页面(因为他们已经提交了)。基本上,它应该使历史堆栈的长度为 1。

But there doesn't seem to be a history.delete(), or history.merge(). The best I can do is a history.replaceState(stage1)which leaves the history stack as: ["selectDate","selectDate"].

但似乎没有history.delete(), 或history.merge()。我能做的最好的是history.replaceState(stage1)将历史堆栈保留为:["selectDate","selectDate"]

How do I get rid of one layer?

我如何摆脱一层?

Edit:

编辑:

Thought of something else, but it doesn't work either.

想到了别的东西,但它也不起作用。

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

This leaves the history stack as ["selectDate","selectDate#foo"].

这将历史堆栈保留为["selectDate","selectDate#foo"].

So, as an alternative, is there a way to remove the 'forward' history without pushing a new state?

那么,作为替代方案,有没有办法在不推送新状态的情况下删除“前进”历史?

回答by Mike B.

You may have moved on by now, but... as far as I know there's no way to delete a history entry (or state).

您现在可能已经继续前进了,但是……据我所知,无法删除历史条目(或状态)。

One option I've been looking into is to handle the history yourself in JavaScript and use the window.historyobject as a carrier of sorts.

我一直在研究的一种选择是自己在 JavaScript 中处理历史并将window.history对象用作各种载体。

Basically, when the page first loads you create your custom history object (we'll go with an array here, but use whatever makes sense for your situation), then do your initial pushState. I would pass your custom history object as the state object, as it may come in handy if you also need to handle users navigating away from your app and coming back later.

基本上,当页面第一次加载时,您会创建您的自定义历史对象(我们将在这里使用一个数组,但使用任何对您的情况有意义的东西),然后执行您的初始pushState. 我会将您的自定义历史对象作为状态对象传递,因为如果您还需要处理用户离开您的应用程序并稍后返回,它可能会派上用场。

var myHistory = [];

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

    //Load page data.
}

Now when you navigate, you add to your own history object (or don't - the history is now in your hands!) and use replaceStateto keep the browser out of the loop.

现在,当您导航时,您将添加到您自己的历史对象(或者不添加 - 历史现在掌握在您手中!)并用于replaceState使浏览器保持在循环之外。

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

    //Load page data.
}

When the user navigates backwards, they'll be hitting your "base" state (your state object will be null) and you can handle the navigation according to your custom history object. Afterward, you do another pushState.

当用户向后导航时,他们将达到您的“基本”状态(您的状态对象将为空),您可以根据您的自定义历史对象处理导航。之后,您执行另一个 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.
    }
}

The user will never be able to navigate forward using their browser buttons because they are always on the newest page.

用户将永远无法使用他们的浏览器按钮向前导航,因为他们总是在最新的页面上。

From the browser's perspective, every time they go "back", they've immediately pushed forward again.

从浏览器的角度来看,每次它们“返回”时,它们都立即再次向前推进。

From the user's perspective, they're able to navigate backwards through the pages but not forward (basically simulating the smartphone "page stack" model).

从用户的角度来看,他们能够在页面中向后导航但不能向前导航(基本上模拟智能手机的“页面堆栈”模型)。

From the developer's perspective, you now have a high level of control over how the user navigates through your application, while still allowing them to use the familiar navigation buttons on their browser. You can add/remove items from anywhere in the history chain as you please. If you use objects in your history array, you can track extra information about the pages as well (like field contents and whatnot).

从开发人员的角度来看,您现在可以高度控制用户如何在您的应用程序中导航,同时仍然允许他们使用浏览器上熟悉的导航按钮。您可以根据需要从历史链中的任何位置添加/删除项目。如果您在历史数组中使用对象,您还可以跟踪有关页面的额外信息(如字段内容等)。

If you need to handle user-initiated navigation (like the user changing the URL in a hash-based navigation scheme), then you might use a slightly different approach like...

如果您需要处理用户启动的导航(例如用户在基于哈希的导航方案中更改 URL),那么您可能会使用稍微不同的方法,例如...

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

回答by jtompl

There is no way to delete or read the past history.

没有办法删除或阅读过去的历史。

You could try going around it by emulating history in your own memory and calling history.pushStateeverytime window popstateevent is emitted (which is proposed by the currently accepted Mike's answer), but it has a lot of disadvantages that will result in even worse UX than not supporting the browser history at all in your dynamic web app, because:

您可以尝试通过在自己的记忆中模拟历史记录并在history.pushState每次popstate发出窗口事件时调用(这是由当前接受的 Mike 的回答提出)来尝试绕过它,但它有很多缺点,会导致比不支持更糟糕的 UX在您的动态 Web 应用程序中完全没有浏览器历史记录,因为:

  • popstate event can happen when user goes back ~2-3 states to the past
  • popstate event can happen when user goes forward
  • 当用户回到过去的 ~2-3 个状态时,可能会发生 popstate 事件
  • 当用户前进时会发生 popstate 事件

So even if you try going around it by building virtual history, it's very likely that it can also lead into a situation where you have blank history states (to which going back/forward does nothing), or where that going back/forward skips some of your history states totally.

因此,即使您尝试通过构建虚拟历史来绕过它,它也很可能会导致您具有空白历史状态(向后/向前不执行任何操作)的情况,或者向后/向前跳过一些你的历史完全说明。