更改 window.location.hash 在历史记录中创建条目但不影响 chrome 后退按钮

changing window.location.hash creates entry in history but doesn't affect chrome back button

更新:- 正如 Akansh Gulati 在他的回答中指出的,如果用户在单击后退按钮之前与页面有任何交互,这将按预期工作,如果用户这样做与页面没有任何交互并按下后退按钮然后历史记录中的任何条目(通过散列更改或通过 history.push/replace )将被忽略,这是 Google Chrome update will stop websites hijacking your browser back button

的一部分

这是有效且合乎逻辑的答案,所以我接受他的回答


我试图在页面加载后显示一个成功的弹出窗口,如果用户按下 android 后退按钮(在这种情况下相当于浏览器后退按钮)我只想关闭弹出窗口(不要想重定向回支付页面 )

当弹出窗口打开时,我在 url 中添加散列,但是当用户按下后退按钮时 chrome 忽略散列并重定向回上一页,而不是仅仅删除散列(在 Firefox 中工作正常)

我有一个工作示例here使用以下HTML代码重现

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
  </head>
  <body onload="test()">
    <button type="button" onclick="window.history.back();">Back</button>
  </body>
  <script type="text/javascript">
    function writeLength() {
      document.body.appendChild(document.createTextNode(window.history.length));
    }

    function test() {
      window.location.hash = 'a';
      setTimeout(function() {
        writeLength();
        window.location.hash = 'b';
        setTimeout(function() {
          writeLength();
          window.location.hash = 'c';
          setTimeout(function() {
            writeLength();
          }, 1500);
        }, 1500);
      }, 1500);
    }
  </script>
</html>

a) 在 chrome

中打开此页面

b) 等到哈希更改为“#c”

c) 然后按浏览器后退按钮

预期的行为是它应该将哈希值改回“#b”,然后再改回“#a” 但它忽略所有哈希更改并重定向回新标签页

这是code

      window.location.hash = 'a';
      setTimeout(function() {
        writeLength();
        window.location.hash = 'b';
        setTimeout(function() {
          writeLength();
          window.location.hash = 'c';
          setTimeout(function() {
            writeLength();
          }, 1500);
        }, 1500);
      }, 1500);

如何模拟正确的行为(如果有的话)?

我在 Mac

上使用 chrome 版本 77.0.3865.90(官方构建)(64 位)

这是行为的 GIF 图片

在此浏览器中,您需要通过 History API 在历史记录中明确设置至少一个状态(尽管不确定原因)。

即使在此 iframe 中,该示例也应该有效。

history.replaceState( {}, '' );

window.location.hash = 'a';
setTimeout(function() {
  console.log( location.hash );
  window.location.hash = 'b';
  setTimeout(function() {
    console.log( location.hash );
    window.location.hash = 'c';
    setTimeout(function() {
      console.log( location.hash );
      console.log( "You can now use your browser's back button" );
      onpopstate = e => console.log( location.hash );
    }, 150);
  }, 150);
}, 150);

你必须明确地设置你的状态,当你点击后退按钮时你可以执行一些代码,像这样:

添加一个函数来获取没有散列部分的window.location.href(因为我们将显式添加它):

function getUrlWithoutTheHash() {
    let url = window.location.href;
    let hash = window.location.hash;
    let index_of_hash = url.indexOf(hash) || url.length;
    return url.substr(0, index_of_hash);
}

添加一个函数来推送状态(而不是使用位置更改哈希,我们使用状态 API 更改它)我们将向状态添加一个属性 is-my-state 以了解是否这是不是我们的状态:

function pushTheState(url) {
    history.pushState({'is-my-state': true, 'url': url}, null, url);
}

并在打开状态时添加一个处理程序,如果这是您的状态之一,您可以执行所需的代码:

window.onpopstate = function(e){
    if(e.state){
        if(e.state.hasOwnProperty('is-my-state')){
            writeLength();
        }
    }
};

最后,您需要像这样更改函数:

function test() {
    pushTheState(getUrlWithoutTheHash() + '#a');    // pushing url with #a
    setTimeout(function() {
        writeLength();
        pushTheState(getUrlWithoutTheHash() + '#b');    // pushing url with #b
        setTimeout(function() {
            writeLength();
            pushTheState(getUrlWithoutTheHash() + '#c');    // pushing url with #c
            setTimeout(function() {
                writeLength();
            }, 1500);
        }, 1500);
    }, 1500);
}

我可以在 Windows 10 上获得您提供的代码,该代码可以在 Chrome 的相同版本上运行,但如果您仍然遇到问题,则以下内容应该有效。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
  </head>
  <body onload="test()">
    <div id="url"></div>
    <button type="button" onclick="window.history.back();">Back</button>
  </body>
  <script type="text/javascript">
    setInterval(function(){
      document.getElementById('url').innerHTML = window.location.href;
    }, 500);
    function writeLength() {
      document.body.appendChild(document.createTextNode(window.history.length));
    }
    function test() {
      history.pushState({},'','#a');
      setTimeout(function() {
        writeLength();
        history.pushState({},'','#b');
        setTimeout(function() {
          writeLength();
          history.pushState({},'','#c');
          setTimeout(function() {
            writeLength();
          }, 1500);
        }, 1500);
      }, 1500);
    }
  </script>
</html>

这应该有效,因为 window.pushState 应该强制将散列更改添加到浏览器的历史记录中。如果要更新导航更改的页面,可以使用 window.onpopstate 事件。例如:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
  </head>
  <body onload="test()">
    <div id="url"></div>
    <button type="button" onclick="window.history.back();">Back</button>
    <span id="lengths"></span>
</body>
  <script type="text/javascript">
    setInterval(function(){
      document.getElementById('url').innerHTML = window.location.href;
    }, 500);
    var last = false;
    var items = [];
    function writeLength() {
    items.push(window.history.length);
       document.getElementById('lengths').appendChild(document.createTextNode(window.history.length));
    }
    function test() {
     history.pushState({},'','#a'); 
      setTimeout(function() {
        writeLength();
        history.pushState({},'','#b');
        setTimeout(function() {
          writeLength();
          history.pushState({},'','#c');
          setTimeout(function() {
            writeLength();
            last = true;
          }, 1500);
        }, 1500);
      }, 1500);
    }
    window.onpopstate = function(){
      if (last) {
        if (window.location.hash == '#a') document.getElementById('lengths').innerHTML = items[0];
        else if (window.location.hash == '#b') document.getElementById('lengths').innerHTML = '' + items[0] + items[1];
        else if (window.location.hash == '#c') document.getElementById('lengths').innerHTML = '' + items[0] + items[1]+ items[2];
      }
    }
  </script>
</html

上面的代码允许用户在最终长度写入文档后导航到页面的不同"states"。

我尝试使用 pushState 进一步更改位置,问题似乎已解决。无需更换状态。条件只是用户必须 clicked/interacted 至少使用屏幕一次。

function test() {
        window.location.hash = 'a';
        setTimeout(function () {
            writeLength();
            window.history.pushState(null, null, `${window.location.pathname}#b`);
            setTimeout(function () {
                writeLength();
                window.history.pushState(null, null, `${window.location.pathname}#c`);
                setTimeout(function () {
                    writeLength();
                }, 1500);
            }, 1500);
        }, 1500);
    }

很奇怪,他们是这样实现的,但解决方案很简单,只需在初始加载时添加即可。

window.history.replaceState({}, document.title, window.location.href);

不需要其他答案中建议的所有计时器和意大利面条代码;)