禁用HTML5历史记录中的前进按钮

17

我正在使用HTML5 History API编写一个单页JavaScript应用程序。该应用程序通过Ajax加载内容,并在前台屏幕上使用屏幕堆栈来内部维护状态信息。

我想启用“后退”按钮进行导航,但我永远不想启用“前进”按钮。

以下是一些快速的信息:

  • 用户只能后退,不能前进
  • 按下浏览器的返回按钮会关闭用户所在页面屏幕并重新加载上一个页面
  • 项目仅面向最新版本的Chrome,因此其他浏览器实现不重要
  • 我仅使用本机JavaScript和jQuery,并且不想使用History.js来完成这个问题

当我加载新屏幕时,我运行以下代码:

history.pushState(screenData, window.document.title, "#");

我通过 jQuery 绑定到 popstate 事件:

$(window).bind("popstate", function(event) {
    if (event.originalEvent.state != null) {
        // Logic that loads the previous screen using my screen stack
    }
}); 
我的应用程序历史管理正常工作,但当我返回时,向前按钮仍然可用。我需要找出如何在popstate事件上从history中删除数据。

我能用replaceState做到这点吗?我不确定如何去做...

4个回答

12

接受的答案解决了禁用前进按钮的问题,但会创建一个新的令人讨厌的问题:“页面导航回”会被重复插入到历史记录中(如答案评论中所示)。

以下是如何解决“禁用前进按钮”问题并避免“重复”的后退按钮问题。

//setup the popstate EventListener that reacts to browser history events
window.addEventListener("popstate",function(event){
     // In order to remove any "forward"-history (i.e. disable forward 
     // button), this popstate's event history state (having been navigated 
     // back to) must be insert _again_ as a new history state, thereby 
     // making it the new most forwad history state. 
     // This leaves the problem that to have this popstate event's history
     // state to become the new top, it would now be multiple in the history
     //
     // Effectively history would be:
     //  * [states before..] ->
     //  * [popstate's event.state] -> 
     //  * [*newly pushed _duplicate_ of popstate's event.state ]
     // 
     // To remove the annoyance/confusion for the user to have duplicate
     // history states, meaning it has to be clicked at least twice to go 
     // back, we pursue the strategy of marking the current history state
     // as "obsolete", as it has been inserted _again_ as to be the most
     // forward history entry. 
     // 
     // the popstate EventListener will hence have to distinguish 2 cases:
     //
     // case A) "popstate event is _not_ an obsolete duplicate"...
     if( typeof event.state == "object" 
         && event.state.obsolete !== true)
     {
         //...so we _firstly_ mark this state as to from now on "obsolete",
         // which can be done using the history API's replaceState method
         history.replaceState({"obsolete":true},"");
         // and _secondly_ push this state _one_more_time_ to the history
         // which will solve the OP's desired "disable forward button" issue
         history.pushState(event.state,"");
     }

     // case B: there is the other case that the user clicked "back" and
     // encounters one of the duplicate history event entries that are 
     // "obsolete" now.
     if( typeof event.state == "object" 
         && event.state.obsolete === true)
     {
         //...in which case we simply go "back" once more 
         history.back() 
         // by this resolving the issue/problem that the user would
         // be counter-intuively needing to click back multiple times.
         // > we skip over the obsolete duplicates, that have been the
         // the result of necessarily pushing a history state to "disable
         // forward navigation"
     }

},false);

这个程序出现了关于“obsolete”未定义的错误,并且似乎并没有改变行为。 - mrg95
1
@mrg95,请将 typeof event.state == "object" 更改为 typeof event.state === "object" && event.state !== null 以使其正常工作。 - Junior

11

不好的部分

要真正禁用前进按钮,您需要能够删除浏览器历史记录,但并非所有JavaScript实现都允许这样做,因为它会允许网站删除整个历史记录,这对用户来说永远不是有利的。

好的部分

这有点棘手,但我想如果你想做自定义历史记录,那么它可能会起作用。你可以在popstate事件中使用pushState将你的实际页面作为最顶层的历史记录条目。我假设你处理历史记录的方式是,窗口永远不会卸载。这使得你能够自己跟踪用户的历史记录:

var customHistory = [];

在加载每个页面时,使用history.pushState(screenData, window.document.title, "#")将其推送,就像之前做的一样。但是,你也要将状态添加到自定义历史记录中:

history.pushState(screenData, window.document.title, "#");
customHistory.push({data: screenData, title: window.document.title, location: '#'});

如果您有一个popstate事件,只需弹出自定义的历史记录并将其推送到最顶部的条目:

window.onpopstate = function(e) { 
  var lastEntry = customHistory.pop();
  history.pushState(lastEntry.data, lastEntry.title, lastEntry.location);
  // load the last entry
}

或者在jQuery中

$(window).on('popstate', function(e) {
  var lastEntry = customHistory.pop();
  history.pushState(lastEntry.data, lastEntry.title, lastEntry.location);
  // load the last entry
});

1
你好,我知道这个回答有点老了,但我想问一下是否有可能只按一次后退按钮就能回到历史记录中,而不是点击两次?因为这样我首先会将相同的URL推入历史记录中,当我使用后退按钮时,它首先弹出已推送的历史记录URL,这需要点击两次后退按钮才能实际返回。如果您能帮助我解决这个问题,那就太棒了。 - Redrif
我已经自己解决了,不需要回复我的评论。非常感谢您提供使用自定义历史记录的建议。 - Redrif
@Redrif,你确定你能做到吗?我也能做到,但有时候你必须再次点击它2次或甚至n次... 你能分享一下你的代码吗? - EugenSunic
在我的答案中,我采用了“通过将popstate事件的状态再次推送作为最前面的历史状态来“禁用前进按钮”的策略”,但是我做到了以下改进:(a) 不需要重复历史记录,(b) 避免了“由于重复历史记录状态而强制用户多次点击后退按钮”的问题。 - humanityANDpeace
我正在做这件事,然后来到这里想找出如何不禁用前进按钮的方法 ;) - ed22

8

只需使用以下jQuery代码禁用前进按钮:

  $( document ).ready( function(){
    history.pushState(null,  document.title, location.href);        
   });

0

注意:

  • 此代码已经测试并且没有显示任何问题,但是我建议开发人员在使用该代码进入生产环境之前进行更多的测试。
  • 如果您的应用程序中使用了HTML5 history.replaceState(),则下面的代码可能无法正常工作。

我创建了一个自定义函数来禁用前进按钮。

以下是代码(它不适用于哈希路由策略):

  <script>
    (function() {
      // function called after the DOM has loaded
      disableForwardButton();
    })();

    function disableForwardButton() {
      var flag, loop = false;
      window.addEventListener('popstate', function(event) {
        if (flag) {
          if (history.state && history.state.hasOwnProperty('page')) {
            loop = true;
            history.go(-1);
          } else {
            loop = false;
            history.go(-1);
          }
        } else {
          history.pushState({
              page: true
            },
            null,
            null
          );
        }
        flag = loop ? true : !flag;
      });

      window.onclick = function(event) {
        flag = false;
      };
    }
  </script>

如已被接受的答案评论中所指出的,问题在于您必须双击后退按钮才能导航回到页面,这很繁琐且不实用。
代码解释:每次单击后退按钮时,您需要创建一个额外的历史记录元素,以便当前所在的页面指向新创建的历史页面。这样就没有页面可以前进了,因为pushState是最后一个状态(将其视为数组中的最后一个元素),因此您的前进按钮将始终被禁用。
引入循环变量的原因是因为您可能会遇到这样的情况:您返回到某个页面并发生pushState代码,从而创建了最后一个历史元素,然后再次返回上一页,这将创建一个额外的历史元素。换句话说,您有类似于以下内容:
[page1、page2、page2、page2]
现在,一旦在page2(索引3元素)上单击返回按钮,您将再次回到page2的索引1元素,而您不希望如此。请记住,您可以拥有一个x page2元素的数组,因此引入了循环假变量来解决这个特定情况,使用它,您可以从page2跳转到页面1,无论数组中有多少page2元素。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接