Using the html5 window.history
API, I can control the navigation pretty well on my web app.
The app currently has two states: selectDate
(1) and enterDetails
(2).
When the app loads, I replaceState
and set a popState
listener:
history.replaceState({stage:"selectDate",...},...);
window.onpopstate = function(event) {
that.toStage(event.state.stage);
};
When a date is selected and the app moves to stage 2 I push state 2 onto the stack:
history.pushState({stage:"enterDetails",...},...);
This state is replaced anytime details change so they are saved in the history.
There are three ways to leave stage 2:
- save (ajax submit)
- cancel
- back button
The back button is handled by the popstate
listener. The cancel button pushes stage 1 so that the user can go back to the details they were entering the back button. These both work well.
The save button should revert back to stage 1 and not allow the user to navigate back to the details page (since they already submitted). Basical, y it should make the history stack be length = 1.
But there doesn't seem to be a history.delete()
, or history.merge()
. The best I can do is a history.replaceState(stage1)
which leaves the history stack as: ["selectDate","selectDate"]
.
How do I get rid of one layer?
Edit:
Thought of something else, but it doesn't work either.
history.back(); //moves history to the correct position
location.href = "#foo"; // successfully removes ability to go 'forward',
// but also adds another layer to the history stack
This leaves the history stack as ["selectDate","selectDate#foo"]
.
So, as an alternative, is there a way to remove the 'forward' history without pushing a new state?
Best Answer
You may have moved on by now, but... as far as I know there's no way to delete a history entry (or state).
One option I've been looking into is to handle the history yourself in JavaScript and use the
window.history
object as a carrier of sorts.Basically, when the page first loads you create your custom history object (we'll go with an array here, but use whatever makes sense for your situation), then do your initial
pushState
. I would pass your custom history object as the state object, as it may come in handy if you also need to handle users navigating away from your app and coming back later.Now when you navigate, you add to your own history object (or don't - the history is now in your hands!) and use
replaceState
to keep the browser out of the loop.When the user navigates backwards, they'll be hitting your "base" state (your state object will be null) and you can handle the navigation according to your custom history object. Afterward, you do another pushState.
The user will never be able to navigate forward using their browser buttons because they are always on the newest page.
From the browser's perspective, every time they go "back", they've immediately pushed forward again.
From the user's perspective, they're able to navigate backwards through the pages but not forward (basically simulating the smartphone "page stack" model).
From the developer's perspective, you now have a high level of control over how the user navigates through your application, while still allowing them to use the familiar navigation buttons on their browser. You can add/remove items from anywhere in the history chain as you please. If you use objects in your history array, you can track extra information about the pages as well (like field contents and whatnot).
If you need to handle user-initiated navigation (like the user changing the URL in a hash-based navigation scheme), then you might use a slightly different approach like...