Restore original page when navigating back after history.pushState - javascript

I'm using history.pushState to create a 'fake' results page after the user submits a form. However, when I navigate back, the visible page stays the same. What is an easy way to get the original page - unmodified by javascript, but with the form filled out - after pressing back?

You need to watch the current address for changes and react to it yourself.
See this example:
https://developer.mozilla.org/en/DOM/window.onpopstate
Also have a look at the documentation:
https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history/

You have to handle the onpopstate event and reload the previous page manually:
window.onpopstate = function() {
location.reload();
};

Related

Return customer to same position when clicking browser's back button

What I am trying to do?
I want to remember the current position of the user on the page, so once s/he navigates to another page and then comes back using the browser "Back" button to scroll to that position. Please note that I need to maintain this behaviour only when the user clicks on the browser back button.
How I am trying to achieve it?
When the user clicks on an href I was using a method to replace the state. The page was then re-directed to the right place.
window.history.replaceState({ scrollToProduct: true, productCode: 'produt-XXXX' }, document.title, `${window.location.href}?product-code=produt-XXXX`);
I was then trying to register popstate listener in my App.vue
beforeMount() {
window.onpopstate = function(e) {
console.log(e);
};
}
Where my issue is?
Unfortunatelly the popstate listener doesn't get attached, so I can't capture the click of the "Back" button.
I've also tried using router.push.
this.$router.push({ name: 'NameOfComponent' });
What is interesting is that I get navigated to the other page (without page reload), and when I click on the back buttton the popstate does work. I am unfortunatelly not able to pass anything into the hisotry state.
Can someone help with this?
PS: I am not using SPA, but rather refresh on each page.
You can't use vue-router if you don't register it correctly or if you don't use Vue as an SPA.
What you need is history.back() or history.go(-1).
https://developer.mozilla.org/en-US/docs/Web/API/History/back

HTML5 History API: "popstate" not called when navigating back from another page

I have to maintain a site with an AJAX search form (for lets say books) and I want the search results to be a part of the browser history.
So I set the "popstate" event handler
window.addEventListener('popstate', function(e) {
alert("popstate: " + JSON.stringify(e.state));
});
and call pushState() upon the successfull AJAX request:
var stateObj = { q: "harry potter" };
window.history.pushState(stateObj, "Result", "/search");
Then my clickflow is:
Load the page with the search form - NORMAL request - path is /
Enter a query string and submit the form - AJAX request - path changes to /search
Click on a search result - NORMAL request - path changes to /books/harry-potter
When I now go back by clicking the browser back button I would expect the "popstate" event to be triggered with the according state object. But nothing happens. - path changes to /search
When I then go back one more time I get an alert with popstate: null - path changes to /
And when I go forward after that, I get popstate: {"q":"harry potter"} - path changes to /search
So, the important popstate event (the one with the query string) gets only triggered when going forward, but not when going back.
Is this because im navigating back from a site, whose history entry has been created automatically instead of programmatically by pushState?
But if so, the History API would only make sense for complete single page applications.
Behaviour is the same in any browser. Hope you can help me out :)
For anyone wanting a little more explanation as to why the popstate event only occurs in certain scenarios, this helped me:
On the MDN popstate event page it states:
A popstate event is dispatched to the window each time the active history entry changes between two history entries for the same document.
and
The popstate event is only triggered by performing a browser action, such as clicking on the back button (or calling history.back() in JavaScript), when navigating between two history entries for the same document.
So it looks like popstate is only called when navigating to different urls within the same document. This works great for SPAs but not for anything else.
As an additional note, the html5 spec for session history doesn't seem to have much clarity as to when the popstate event is called:
The popstate event is fired in certain cases when navigating to a session history entry.
Which may indicate that different browsers will treat it differently.
I would love more insight from anyone else

Prevent the URL from changing when popstate occurs

Hi I'm trying to implement a "Are you sure you want to leave this page?" popup when navigating away from a page with a modified form that hasn't been submitted. The website is a single page app built using a custom framework.
Since all anchor links are handled by the framework it can tell that clicking a side nav link will cause a page change and show a Bootstrap confirm dialog. If the user clicks "Ok" the click goes through and an AJAX call is made to pull the new page content. If they click "Cancel" the modal is dismissed and they stay on the current page.
The part I haven't been able to solve is when a user clicks the "Back" button in their web browser. This triggers the "popstate" event. All navigation/history is managed using the history API since it's a single page app.
The issue is that when someone clicks "Back", the URL changes since "popstate" occurred, then I show my "Are you sure you want to leave?" modal. When the event occurs the URL in your address bar now shows the page you would go to from your history, not the page you're currently viewing. If you click "Cancel" in the Bootstrap confirm you are now left with the wrong URL displayed in your address bar.
event.preventDefault, event.stopPropagation, or return false inside popstate don't stop the URL from changing. AFAIK you can't intercept the "Back" button before "popstate". Trying to window.history.replaceState inside "popstate" doesn't seem to work either.
Does anyone have a solution on how to make this work?
Can you stop the URL from changing in the "popstate" event?
Can you rewrite the history somehow in "popstate" to change the URL back to what it was on the current page and retain the previous entry if you do decide you want it to go through after a modal has been accepted?
Some other solution I haven't thought of?
My original thought was to block the URL change in "popstate" then let the framework trigger the link click to load the new content and change the URL if they click "Yes" but I haven't found a way to do that.
Thanks!
I solved this by doing the following;
Each time a history state is created I generate a timestamp as a guid
The current timestamp is stored in a variable on the top level module
When popstate occurs the incoming history state's guid is compared against the top level modules guid
If the new guid is greater we're going forward, if it's less we're going backward
When the user clicks the back/forward button the hash does change to the incomming URL. If the user clicks cancel it runs history.go( direction ) where direction is 1 or -1 depending on the timestamps. This sets the URL back to what it should be and doesn't do anything weird to the history stack. The top level history variable has a flag to know that we're faking a page change so the link load logic in popstate is not executed.
The only quirk is if the user does click yes to navigate away when that request is sent and returned and a history object is created you do not update the top level module's guid. If you do the guid comparison will always think you're going backward because the new event you just added will always be the highest number. This may not be an issue if you don't make new requests for history URL's but our framework does so it doesn't display stale data like the browser typically does.
It's not ideal since you do see the URL change (no content changes) but so far it seems to work well enough. Another solution I found was to store your own site's history in local/session storage and compare URLs instead of using guid timestamps to find the popstate direction. This won't work in Safari's private browsing mode since both storage layers are disabled so I opted for guid's.

How to trap the back button and refresh the page using ajax

I have a web page which updates progressivly therefore I need the windows "back" button to call a function to refresh the data and not go back a page.
I have the first part working I trap the "back" button with
window.onbeforeunload = confirmExit();
and confirmExit()
function confirmExit() {
switch (page) {
case 2:
countries();
break;
case 3:
counties(page3id)
break;
case 4:
cities(page4id)
break;
}
}
Which all works well but after the page is refreshed the back button default action kicks in and it loads the previous page. I have tried returning true and false. I would appreciate any help, thank you.
you need to add virtual sites to the browsers history. this way the back button will not lead to another website.
use history.pushState() to add virtual sites.
Suppose http://mozilla.org/foo.html executes the following JavaScript:
var stateObj = { foo: "bar" };
history.pushState(stateObj, "page 2", "bar.html");
This will cause the URL bar to display
http://mozilla.org/bar.html, but won't cause the browser to load
bar.html or even check that bar.html exists.
Suppose now that the user now navigates to http://google.com, then
clicks back. At this point, the URL bar will display
http://mozilla.org/bar.html, and the page will get a popstate event
whose state object contains a copy of stateObj. The page itself will
look like foo.html, although the page might modify its contents during
the popstate event.
If we click back again, the URL will change to
http://mozilla.org/foo.html, and the document will get another
popstate event, this time with a null state object. Here too, going
back doesn't change the document's contents from what they were in the
previous step, although the document might update its contents
manually upon receiving the popstate event.
from MDN: Manipulating the browser history
the user can now press the back button the number of times you added virtual sites. to detect the back button press use window.onpopstate
The popstate event
A popstate event is dispatched to the window every time the active
history entry changes. If the history entry being activated was
created by a call to pushState or affected by a call to replaceState,
the popstate event's state property contains a copy of the history
entry's state object.
from MDN: Manipulating the browser history

When the back button triggers popState how can I prevent a page refresh?

I am trying to modify the content in my page without a reload. Currently my code reads:
window.onpopstate = function(event){
// Ajax Request the Page and replace content with new content
};
This works when I push a state then trigger the popstate event, but if I press the back button in the browser it navigates to the url instead of calling my onpopstate event. How can I prevent a page refresh and update the page with my ajax call instead?
edit: I am trying to update with pushState and popstate. I was hoping to keep my urls hash free.
You have to make sure there is always a history state you've pushed from the current page on the history to prevent the back button from performing a page load.
If you're trying to keep the user "contained" in your web app so the back button always provides some kind of function, you need to push at least two states onto the stack and then make sure to push another state from your popstate handler.
var foo = {foo: true}; // state object
history.pushState(foo, "unused argument", "#newInitialUri");
...
var bar = {bar: true}
history.pushState(bar, "unused argument", "#newStateOfWebApp");
...
window.onpopstate = function(event){
...
var baz = {baz: true}
history.pushState(baz, "unused argument", "#baseState");
};
In the above example say we loaded '/'. The script starts executing and the browser window URI changes to '/#newInitialUri' but no page load occurs. Then immediately after, the browser URI changes to '/#newStateOfWebApp' and no page load occurs.
The user pushes the back button on their browser. Your popstate handler fires. During your handler, event.state equals foo and the browser uri is '/#newInitialUri'. No page load occurs. The handler finishes completing, calling history.pushState and now the browser uri is '/#baseState'. No page load occurs. If the user clicks back again, your popstate event will fire again, event.state will equal foo (again), the browser uri will be '/#newInitialUri' (no page load) and then it will be '/#baseState' again (no page load).
The important thing to remember is that the event.state in your popstate handler always contains the state object for the URI you've just come back to, not the one you just came from. This was confusing to me at first, but made sense when I thought about it. For example, the user may have just come back to your page after perhaps having gone off to Google. The state object is your opportunity to communicate the status of your app to your code.
Keep in mind that some browsers fire the popstate event on page load (which is what's supposed to happen according to the spec from my understanding). Be sure to check for your state object in your handler before executing your code to make sure you don't run code you don't intend to on a page load.
One final note: if you're using jQuery to handle events, you'll need to use event.originalEvent.state to refer to the state object.
This may help
The unload event is sent to the window element when the user navigates away from the page. This could mean one of many things. The user could have clicked on a link to leave the page, or typed in a new URL in the address bar. The forward and back buttons will trigger the event. Closing the browser window will cause the event to be triggered. Even a page reload will first create an unload event.
Reference
http://api.jquery.com/unload/
untested
$(window).unload(function(e){
e.preventDefault();
$(window).trigger('popstate ');
});
$(window).bind('popstate ',function(){
//your ajax call here
});
and finally here is a DEMO click on browser's back button to see it working
update
you are right the unload be canceled but you can do some thing like
$(window).unload(function(e){
e.preventDefault();
$(window).trigger('beforeunload');
});
$(window).bind('beforeunload',function(){
alert('call your ajax here');
return '';
});
yet another DEMO

Categories