Search code examples
javascriptbrowser-history

JS: Erroneous popstate event when trying to navigate back past the initial page load


I'm trying to implement JS history using pushState/popState. Navigating back and forward works just fine, but I have trouble navigating before the initial page load using the browser's back button. It needs 1 extra hit on the browser's back button to leave the page. Why is that?

function action(text) {
  history.pushState({"text":text}, text);
  doAction(text);
}

function doAction(text) {
  $('span').text(text);
}

var $button = $('button');
var $p = $('p');

$p.hide();

action("foo");
$button.on('click', function(){
  action("bar");
  $button.hide();
  $p.show();
})

window.addEventListener("popstate", function(e) {
  if (e.state !== null) {
      $button.show();
      $p.text("Next back should navigate away from this page");
  } else {
      $p.text("Still here? Why is that? Next back will really navigate away");
  }
});

https://jsfiddle.net/lilalinux/p8ewyjr9/20/

Edit: Tested with Chrome OS/X


Solution

  • The initial page load shouldn't use history.pushState because it would add another history entry. There is alredy an implicit first history item with state null.

    Using history.replaceState for the initial page load, sets a state for that item but doesn't add another one.

    var initialPageLoad = true;
    function action(text) {
      if (initialPageLoad) {
        // replace the state of the first (implicit) item
        history.replaceState({"text":text}, text);
      } else {
        // add new history item
        history.pushState({"text":text}, text);
      }
      initialPageLoad = false;
      doAction(text);
    }
    
    function doAction(text) {
      $('span').text(text);
    }
    
    var $button = $('button');
    var $p = $('p');
    
    $p.hide();
    
    action("foo");
    $button.on('click', function(){
      action("bar");
      $button.hide();
      $p.show();
    })
    
    window.addEventListener("popstate", function(e) {
      if (e.state !== null) {
          $button.show();
          $p.text("Next back should navigate away from this page");
    //  } else {
    // won't happen anymore, as the first item has state now
    //      $p.text("Still here? Why is that? Next back will really navigate away");
      }
    });