5

Question

Given navigation from /page.html#A to /page.html#B, is there a way to distinguish between a user:

  1. Clicking the browsers's 'back' button, and
  2. Manually changing the url back to /page.html#A ?

Background / Context

I'm building a web app, where a single page transitions between multiple slides of content, each identified by a specific location hash, eg '#A', '#B'.

For example, when the user is on slide 'A', and selects option 'B', the location changes from /page.html#A to /page.html#B.

After the transition, location.hash==#B, and back() (either via JS or browser button) would return the user to location.hash==#A.

However, there is nothing to prevent a user from manually changing the hash in the URL bar. In this case the browser would consider this a navigation forward, inserting /page.html#B in the back history. That is, navigation history would be #A > #B > #A and clicking back would now take a user to #B.

I need to distinguish between these two cases so that when I know the user has manually updated the url hash, I can trigger go(N) to synchronise the browser back/next state.

Attempts so far

1) HTML5 popstate event:
I had hoped that the html5 popstate event ( https://developer.mozilla.org/en-US/docs/Web/Events/popstate ) would only be fired for case#1, but I can confirm it fires in both cases.

2) Browser .onhashchange event
I can confirm that if present, the event is fired in both cases

3) jQuery mobile hashChange() I can confirm is fired in both cases

4) Read browser navigation history
My next thought would be to maintain a JS array of hash history, and compare whether the new hash and browser history match the JS array, but JS can't read the browser location history for security reasons.

Thoughts

I know that if I call window.history.forward(), and no page exists, nothing happens. I'm thinking a JS array of hash history, calling forward(), checking the new location.hash (as security now allows it), comparing to JS array, then calling go(N) to synchronise the browser back/next state. But it's a bit messy.

zero-day
  • 362
  • 2
  • 13

2 Answers2

0

As there is no back button event in javascript, the best I can recommend is creating your own back button on your page

Look at: How to Detect Browser Back Button event - Cross Browser

Community
  • 1
  • 1
Ayo K
  • 1,719
  • 2
  • 22
  • 34
  • 1
    I can confirm this does not distinguish between the two cases. In both cases, whether a user clicks 'back', or manually changes the hash in the url, the jQuery 'hashchange' event is fired. – zero-day Feb 19 '16 at 10:02
  • what are you trying to achieve in the case where (1) the back button is clicked, (2) user changes the URL manually? – Ayo K Feb 19 '16 at 10:35
  • each hash represents a form. Consider a case where user has navigated from [A>B>C]. In case(1), clicking 'back' takes user to #B, and browser state is [A>B], which is correct. In case(2), user types in #B, and browser history becomes [A>B>C>B]. Now if user clicks 'back, they go to #C rather than #A. It's important because only a subset of paths is valid, eg valid=[ABC, ACD, AD], all others need to be normalised. If I can detect case(2), I can get distance between duplicates, eg [A>B>C>B], (distance between Bs = 2), then execute go(-3) + hash=B to generate browser history [A>B] – zero-day Feb 19 '16 at 10:59
  • The problem with the new article you cite is that it still doesn't distinguish case (1) from (2). It only distinguishes browser manipulation from in-page manipulation. The app I'm working on already handles the in-page manipulation fine, using a simple call to back(), forward() or location.hash, depending on user action. It appears it is possible to distinguish case(1) from (2) by programmatic derivation by maintaining an array of history. I'll post one possible solution when it's finished in a few days, unless someone else has a quicker solution in the meantime. – zero-day Feb 19 '16 at 13:28
0

Yes, you can distinguish between:

  1. Clicking the back()/forward() browser button, and
  2. Manually editing the location.hash in the browser URL bar

It is also possible to use both alongside in-page HTML element navigation.

Issues:

  1. Browser back(), forward() and go() calls do not immediately update location.hash. It is required to wait ~10ms via setTimeout() to let browser finish the navigation and update location.

Solution (pseudo-code):

  • Maintain an array of backward_history_hashes (note 'backwards' means logically, not temporally)
  • Maintain a value of current_location.hash
  • Maintain an array of forward_history_hashes
  • Maintain a boolean flag for in-page navigation, default to FALSE
  • Maintain a boolean flag whether to ignore_hash_change
  • Create a setTimeout() monitor to check for location.hash changes

In each case, the history arrays are simple string arrays of location.hashes

on_in_page_navigation()

  • set in_page_flag = true
  • trigger browser navigation via back(), forward() or go(N)
  • set in_page_flag = false

on_location_hash_change()

  • set ignore_hash_change = true
  • if( ! in_page_flag) rewrite_browser_history()
  • display content corresponding to new location.hash
  • set ignore_hash_change = false

rewrite_browser_history()

  • just assume that it was a manual URL edit, and use JS history arrays to trigger back() and forward() calls to generate the desired browser-history
  • execute go(N) to desired location.hash to synchronize browser-history with JS history arrays
zero-day
  • 362
  • 2
  • 13