Question

In my one-page web app, I would like to have a back button on some of the pages that takes the user back to a specific point in their browser history: not necessarily the previous page.

Note that I am not talking about the browser's back button, which should work as usual.

Use Case

For an example, consider the gmail web app. There is a folder view, potentially with search filters or other parameters, and then a message view that shows a single message. In the messages view, there is a back button that does not take you to the previous page: even if you have clicked around reading several messages, the back button will take you back to the folder view from which you originally came before clicking on any of the messages.

In the case of gmail, this is achieved by simply encoding the previous state at the end of the URL. E.g., if you got to this message by searching, it will be:

#search/stuff/<MESSAGE_ID>

From this, the app knows that the back button should point to the page with:

#search/stuff

In my application, however, there is too much state to encode it all in the URL. Rather than two views (folder + message), there are three views, let's call them A, B, and details, with both A and B having a wide array of possible filters and options encoded in the URL that I would like to preserve when e.g. returning from B to A or from details to B. Encoding all the parameters for A, B and details in the URL of the details page would make the URL extremely unwieldy.

Implementation

I thought this could be easily achieved using the html5 history API. However, as far as I can see the history API does not provide support for reading the URLs in the history: you can only blindly go back or forward.

history.js provides access to past states, as discussed in a related question:

History API: Javascript Pushstate, get previous URL

However, I am using angularjs which does not work well with history.js, because it talks directly to the history api instead of going through history.js, so the states from transitions caused by the angular $location service do not show up in history.js' History object.

It seems that what I would need to do one of the following:

  • get history.js to work well with angular
  • re-implement a subset of history.js' functionality in my own code

I also considered using document.referrer to peek at the previous value in history, but that does not work as it does not get set when navigating within a one-page app.

Was it helpful?

Solution

Answering my own question, I chose to go for the simpler solution suggested by @MaxArt in his comment.

  • Generate a random ID for a page
    • Since I use angularjs, I do this $on('$routeChangeSuccess') or $on('$routeUpdate')
    • without angularjs, I suppose I would do this onpopstate
  • Store a mapping from this random ID to all the URL information that I need (in my case, search and path) in sessionStorage
  • Include a search parameter from=the ID of the current page in outgoing links that go forward in the conceptual hierarchy of my app
  • When the custom back button is clicked, look up the state I come from in sessionStorage using the from seach parameter
    • if found, go back to that URL
    • if not found (user navigated to this page directlry), go back to the default URL for the previous view in the app's hierarchy

Rationale for taking this approach over the more general approach of building a history of past URLs:

  • As mentioned, the pushState API does NOT provide access to past URLs
  • Integrating History.js, which does provide that information, into angularjs does not seem trivial
    • angularjs uses the history API internally: it would need to be changed to instead use History
  • Implementing custom code to record the URL history in sessionStorage is also not trivial. The main problem is that, lacking integration with the browser's history, there does not seem to be a general way to know if a visited page is new or it was reached by going back one or more steps in the browser history. I'd be happy to be corrected on this if someone can suggest a solution (1)
  • Using History.js or any equivalent solution that wraps all history.pushState with additional code, requires pretty much all links on the page to be wrapped in History.pushState, otherwise URL changes do not show up in the History.

(1) How do I retrieve if the popstate event comes from back or forward actions with the HTML5 pushstate?

OTHER TIPS

I've never worked with angular.js, but presumably window.history.pushState is what you're looking for if you want something guaranteed to work. Have a read-over of http://spoiledmilk.com/blog/html5-changing-the-browser-url-without-refreshing-page/ for the detailed information on what this baby can do.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top