在我的AJAX应用程序中拦截对后退按钮的调用:我不希望它做任何事情

Zac*_*urt 72 javascript ajax cross-browser browser-history

我有一个AJAX应用程序.用户单击按钮,页面的显示会发生变化.他们点击后退按钮,期望进入原始状态,但是,他们会在浏览器中转到上一页.

如何拦截和重新分配后退按钮事件?我已经研究过像RSH这样的库(我无法工作......),我听说使用散列标签会有所帮助,但我无法理解它.

jps*_*ons 116

啊,后退按钮.您可能会想象"返回"会触发一个JavaScript事件,您可以像这样简单地取消:

document.onHistoryGo = function() { return false; }
Run Code Online (Sandbox Code Playgroud)

不是的.根本没有这样的事件.

如果你真的有一个网络应用程序(而不仅仅是一个具有一些ajaxy功能的网站),接管后退按钮是合理的(如你所提到的那样,URL上有片段).Gmail就是这么做的.我在谈论你的网页应用程序在一个页面中的所有内容,都是自包含的.

该技术很简单 - 每当用户采取修改事物的操作时,重定向到您已经使用的相同URL,但使用不同的哈希片段.例如

window.location.hash = "#deleted_something";
...
window.location.hash = "#did_something_else";
Run Code Online (Sandbox Code Playgroud)

如果您的Web应用程序的整体状态是可清除的,那么这是一个使用哈希的好地方.假设您有一个电子邮件列表,也许您将连接所有ID和读/未读状态,并使用MD5哈希作为您的片段标识符.

这种重定向(仅限哈希)不会尝试从服务器获取任何内容,但它会在浏览器的历史列表中插入一个插槽.因此,在上面的示例中,用户点击"返回",他们现在在地址栏中显示#deleted_something.他们再次回击,他们仍然在你的页面上,但没有哈希.然后再回来,他们实际上回到了他们来自哪里.

现在是困难的部分,当用户回击时让你的JavaScript被检测到(所以你可以恢复状态).您只需观察窗口位置并查看其何时发生变化.随着民意调查.(我知道,哎呀,民意调查.好吧,现在没有更好的跨浏览器了).您将无法判断他们是前进还是后退,因此您必须使用您的哈希标识符获得创意.(也许是与序列号连接的哈希...)

这是代码的要点.(这也是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);
Run Code Online (Sandbox Code Playgroud)

  • 您可能应该在第一个代码块中添加注释,例如“//不起作用”或类似的内容,以便简要浏览答案的人知道您没有提供该解决方案。 (2认同)

git*_*rik 49

为旧的(但受欢迎的)问题提供最新答案:

HTML5引入了history.pushState()history.replaceState()方法,分别允许您添加和修改历史条目.这些方法与window.onpopstate事件一起使用.

使用history.pushState()更改在XMLHttpRequest更改状态后创建的对象的HTTP标头中使用的引用者.引用者将是其窗口this在创建XMLHttpRequest对象时的文档的URL .

来源:从Mozilla Developer Network 处理浏览器历史记录.


Mar*_*sen 10

使用jQuery我做了一个简单的解决方案:

$(window).on('hashchange', function() {
    top.location = '#main';
    // Eventually alert the user describing what happened
});
Run Code Online (Sandbox Code Playgroud)

到目前为止只在Google Chrome中测试过.

这解决了我的网络应用程序的问题,这也是基于AJAX的高度.

它可能有点hack'ish - 但我称之为优雅的黑客;-)每当你尝试向后导航时,它会在URI中弹出一个哈希部分,这在技术上是它试图向后导航的内容.

它拦截了单击浏览器按钮和鼠标按钮.而且你不能通过每秒点击几次来向后强制它,这是基于setTimeout或setInterval的解决方案中可能出现的问题.


Luk*_*uke 8

我非常感谢darkporter的回答中给出的解释,但我认为可以通过使用" hashchange "事件来改进.正如darkporter所解释的那样,您希望确保所有按钮都更改window.location.hash值.

  • 一种方法是使用<button>元素,然后将事件附加到它们然后设置window.location.hash = "#!somehashstring";.
  • 另一种方法是使用按钮链接,例如<a href="#!somehashstring">Button 1</a>.单击这些链接时,哈希会自动更新.

我在哈希标志之后有一个感叹号的原因是为了满足谷歌的"hashbang"范式(更多关于这一点),如果你想被搜索引擎索引,这很有用.您的hashstring通常是名称/值对,#!color=blue&shape=triangle或类似列表#!/blue/triangle- 对您的Web应用程序有意义.

然后你只需要添加这段代码,只要哈希值发生变化(包括击中后退按钮时)就会触发.似乎没有必要进行轮询循环.

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

除了Chrome 36之外,我还没有测试任何东西,但根据caniuse.com的说法,这应该适用于IE8 +,FF21 +,Chrome21 +以及除Opera Mini以外的大多数其他浏览器.


Rob*_* 山本 6

很容易禁用浏览器BACK按钮,就像下面的JQuery代码一样:

// 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;
    }
  });
}
Run Code Online (Sandbox Code Playgroud)

你可以看到它工作,更多的例子在这里 dotnsf 和这里 thecssninja

谢谢 !

  • 这是该页面上唯一在 Chrome v52.0 中对我有用的代码片段。谢谢。 (2认同)

Bri*_*ian 5

我摸索出了一种覆盖正常的历史行为,创造独特backforward按钮事件,使用HTML5历史API(它不会在IE 9运行)。这是非常hacky,但如果你想拦截后退和前进按钮事件并根据需要处理它们是有效的。这在许多情况下可能很有用,例如,如果您正在显示远程桌面窗口并且需要在远程机器上重现后退和前进按钮点击。

以下将覆盖后退和前进按钮行为:

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

如果您在这些回调函数中的任何一个中返回 false,您将允许浏览器继续执行正常的后退/前进操作并离开您的页面。

这是所需的完整脚本:

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.
        }
    });
};
Run Code Online (Sandbox Code Playgroud)

这通过一个简单的概念起作用。当我们的页面加载时,我们创建了 3 个不同的历史状态(编号为 1、2 和 3)并将浏览器导航到状态 2。因为状态 2 在中间,下一个历史导航事件将使我们处于状态 1 或3,由此我们可以确定用户按下的方向。就像那样,我们拦截了一个后退或前进按钮事件。然后我们根据需要处理它并返回到状态 2,以便我们可以捕获下一个历史导航事件。

显然,在使用 HistoryButtonOverride 脚本时,您需要避免使用 history.replaceState 和 history.pushState 方法,否则您会破坏它。