6

The history.back() function is supposed to take me back one step in the history created using HTML5 history API. The following code works as expected in Firefox but does not in Chrome:

<html>
    <script type="text/javascript">
        history.replaceState({path: '/home'}, '', '?page=home');
        history.pushState({path: '/second'}, '', '?page=second');
        console.log(history.state.path); // says "/second"
        history.back();
        console.log(history.state.path); // says "/second" but should say "/home"
    </script>
</html>

In Chrome, it prints /second both times, whereas after going back it should print /home. Am I missing something?

Donald Duck
  • 6,488
  • 18
  • 59
  • 79
ccrisan
  • 359
  • 3
  • 8
  • Does it have the same effect when using `history.go(-1);` instead of `history.back();`? – TheRealVira Oct 16 '16 at 10:10
  • Yes, both `history.back()` and `history.go(-1)` behave exactly the same. – ccrisan Oct 16 '16 at 10:15
  • Normally this fixes it... There was also some sort of chrome thing, where you need to `return false` after calling `history.go(-1);`. – TheRealVira Oct 16 '16 at 10:20
  • The only thing that fixes this for me in a way is "delaying" using setTimeout, by at least 10ms, which is not a solution. – ccrisan Oct 16 '16 at 10:23
  • That's interesting... the HTML5 API was always a bit odd, so I'd suggest committing a bug. There are also a bunch of solutions when googeling "history.back not working chrome", but most of them are based on using the `back` method and `returning false`. – TheRealVira Oct 16 '16 at 10:31
  • `returning false` does make sense indeed, but only when calling it from a click handler on a link element that would otherwise change the location by itself to the `href` attribute. – ccrisan Oct 16 '16 at 10:39

1 Answers1

2

In Chrome, history.back / history.forward / history.go are not synchronized with history.state. I encountered this issue a while back while working on PDF.js, and reported it at Chromium's issue tracker: https://crbug.com/510026. Nothing has come out of it though.

A work-around is to use the popstate event to detect when the navigation finishes:

history.replaceState({path: '/home'}, '', '?page=home');
history.pushState({path: '/second'}, '', '?page=second');
console.log(history.state.path); // says "/second"

addEventListener('popstate', function(event) {
  console.log(history.state.path); // says "/home"
  // You can also use console.log(event.state);
}, {once: true});
history.back(); // Will asynchronously change history.state

Note: addEventListener with the once parameter is only supported as of Chrome 55 - before that you have to call removeEventListener in the listener if you want a listener to run once, like this:

addEventListener('popstate', function listener(event) {
  removeEventListener('popstate', listener);
  console.log(history.state.path); // says "/home"
  // You can also use console.log(event.state);
});
Rob W
  • 315,396
  • 71
  • 752
  • 644
  • My problem with this approach is that the code relying on the new state is completely synchronous. Making it asynchronous would make it ugly and simply wouldn't make sense. I'm still waiting for a fix for this. Until then, I have disabled the entire related functionality in Chrome. – ccrisan Oct 22 '16 at 11:48