When navigating sites, users sometimes encounter the popup below when navigating away from a page.
This confirmation is important in times when data might be lost when leaving a page.
Normally, this is done by:
window.onbeforeunload = -> 'Your new changes will be lost.'
However, Backbone applications are single-page applications. In this case, moving to another page in Backbone does not reload a page.
Page navigations are managed by Backbone and unfortunately, this does not trigger the beforeunload
event. It will only trigger when closing the window/tab or pressing the “Refresh” buttons in the browser, not when pressing “Back” or navigation links on the page. Therefore, page transition guard is incomplete (and this is not good).
Solution
To solve this problem, Veracross’ backbone-beforepopstate can be used. This is dependent on Backbone.js and jQuery so make sure to include them, too.
backbone-beforepopstate adds 4 events that are triggered during Backbone navigation.
popstate
- triggered by pressing ‘Back’ button on browserpushstate
- triggered by navigating by interacting with page elementsbeforepopstate
- triggered before page transition is applied onpopstate
beforepushstate
- triggered before page transition is applied onpushstate
Since what we’re trying to achieve is to guard page transition before it actually happens, beforepushstate
and beforepopstate
will be used.
Activating backbone-beforepopstate
After loading Backbone and jQuery, call
Backbone.addBeforePopState(Backbone)
This will override Backbone’s default navigation methods to add mechanisms to trigger the above events.
Constructing the handlers
One thing to keep in mind for handlers is that if it returns null
, it will not guard the page transition (popup won’t be shown). On the other hand, if a string is returned, this string will be shown in the popup.
Handlers can be attached using the following code:
guardPage = (e) -> 'Your new changes will be lost.'
dontGuardPage = (e) -> null
# Will guard page transition
$(window).on('beforepopstate beforepushstate', guardPage)
# Will not guard page transition (since handler returns null)
$(window).on('beforepopstate beforepushstate', dontGuardPage)
After this, pressing “Back” button and navigating by interacting with page elements are now guarded!
NOTE: Popup for beforepopstate
and beforepushstate
uses window.confirm()
so instead of the “Leave this Page” and “Stay on this Page” buttons, it shows “OK” and “Cancel”.
How about onbeforeunload
?
The same handlers can be used for beforeunload
event
$(window).on('beforepopstate beforepushstate beforeunload', guardPage)
Finally, the page is fully guarded!
More tips
-
The different events received by a single handler can be differentiated by the
type
property. Hence, if a slightly different behavior is needed for a certain event, this can be used. -
Events from backbone-beforepopstate have a
fragment
property. This is the URL fragment that the application would like to transition to. If only certain transitions should be permitted, this URL can be compared with the current URL to show the popup correctly.
# Questionnaire path: `/question/:question_number`
guardQuestionnaireExit = (e) ->
currentUrl = Backbone.history.fragment
toUrl = e.fragment
prompt = 'Your answers to the questionnaire will be lost.'
questionnairePathRegex = /^question\//
# Do not guard if currently not in questionnaire
return unless questionnairePathRegex.exec(currentUrl)
# Guard if answering questionnaire and closing/refreshing window
return prompt if e.type == 'beforeunload'
# Guard if going to a page other than questionnaire from questionnaire
return prompt unless questionnairePathRegex.exec(toUrl)
$(window).on('beforepopstate beforepushstate beforeunload', guardQuestionnaireExit)