We have a survey in a single-page app. Because it's a survey, we want to allow users to go back but not forward in history. That is, if they change an answer in the "past" it may affect questions in the future, so the rule is: you can go time travel backwards, but only progress forward in real time.
Because of the nature of this, using history.pushState and popstate handlers isn't quite what we want. In effect, we want an event listener on the browser's back button, while choosing to prevent or allow the "natural" browser's functionality.
I've tried a few solutions, and they do pretty much everything I want them to, except they don't seem to work in Safari. Safari is the new IE, apparently. I've tried these basic code snippets:
window.history.pushState(null, null, document.URL);
window.addEventListener('popstate', () => {
// This is simplified, obviously...
if (onFirstPageOfSurvey) {
// beacuse we start by pushing on the history stack, we have to go back
// 2 steps to allow the user to go back to the actual previous page.
history.back(2);
} else {
// pushing right back on to the history after popping should
// keep the page from navigating away (just not in safari)
window.history.pushState(null, null, document.URL)
}
})
I've tried a number of variations of the above, but can't possibly list them all.
This one seems a bit dated, but does still work in Chrome, but again not Safari
function noBack() {
window.history.forward()
}
noBack()
window.onload = noBack
window.onpageshow = function (evt) {
if (evt.persisted) noBack()
}
window.onunload = function () {
void 0
}
Any help is appreciated!
I know this is a touchy UX subject. The goal is basically to allow the back button to go back within the app, and then if they back out all the way, let the browser history take over and actually go back a page. We're not trying to "trap" the users; on the contrary, we've found that users tend to assume that they can use the back button and are pretty upset once they've gotten well into the survey, and then end up on the page before the survey, and basically lose everything.
Posting to SO was a sort of rubber duck for me...I thought of a few things while writing and actually have a solution at this point.
I think the problem is that Safari doesn't like an immediate
pushState. Upon closer inspection, navigating from home => sruvey results in my Chrome history looking like (top is most recent):...entry #2 being the initial navigation, and entry #1 (most recent) being the result of the first
pushState. In Safari, the exact same navigation just looks likeThe solution:
Defer
pushStateuntil navigating through the app. Declare a flag so that it's only called once. I'm getting the sense it might need to be tied to a user event in Safari. Using asetTimeouton page load did not work.Simplified example: