Tripurari Shankar
Tripurari Shankar

Reputation: 3548

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

Update :- As Akansh Gulati Pointed out in his answer if user have any interaction with the page before clicking back button this will work as expected and if user do not have any interaction with the page and press back button then any entry in history ( either by hash-change or by history.push/replace ) will be ignored which is part of Google Chrome update will stop websites hijacking your browser back button

This is valid and logical answer so I am accepting his answer


I am trying to show a success popup after page load and if user press android back button ( which is in this case equivalent to browser back button ) I only want to close the popup ( don't want to redirect back on payment page )

I am adding hash in url when popup is open but when user press back button chrome ignore the hash and redirect back on previous page instead of just removing the hash ( working fine in Firefox )

I have a working example here use following HTML code to reproduce

<!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) open this page in chrome

b) wait till hash is changed to '#c'

c) then press browser back button

expected behavior is it should change the hash back to '#b' and then back to '#a' but it ignore all the hash changes and redirect back to new-tab page

This is the 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);

how can I simulate the correct behavior (if there is any way) ?

I am using chrome Version 77.0.3865.90 (Official Build) (64-bit) on Mac

here is a GIF image of behavior

actual behavior GIF image

Upvotes: 5

Views: 5873

Answers (4)

Akansh
Akansh

Reputation: 1785

I tried using pushState for further location changes and the problem seems solved. No need to replaceState. The condition is only that user must have clicked/interacted atleast once with screen.

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);
    }

Upvotes: 4

Brandon
Brandon

Reputation: 107

I'm able to get the code your provided working on the same version of Chrome on Windows 10, but if you're still having issues the following should work.

<!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>

This should work because window.pushState should force the hash change to be added to the browser's history. If you want to update the page of a navigation change, you can use the window.onpopstate event. For example:

<!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

The code above allows the user to navigate to different "states" of the page after the final length has been written to the document.

Upvotes: 0

Fawaz Ibrahim
Fawaz Ibrahim

Reputation: 181

You have to set your state explicitly, and you can execute some code when you click the back button, like this:

Add a function to get the window.location.href without the hash part (because we will add it explicitly):

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);
}

Add a function to push state (rather than changing the hash using the location, we change it using the state API) and we will add an attribute is-my-state to the state to know if this is our state or not:

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

And add a handler when you open the state, and if this was one of your states, you can execute a desired code:

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

Finally, you need to change your function like this:

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);
}

Upvotes: 0

Kaiido
Kaiido

Reputation: 136658

In this browser you need to explicitly set at least one state in the History through the History API (not sure why though).

The example should work even in this 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);

Upvotes: 5

Related Questions