Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to tell whether the forward or backward button was pressed using the popstate event

I've been doing some research and it appears that the popstate event fires any time that the history is changed, but there doesn't seem to be a built in way to determine whether the user clicked the back button or the forward button in the browser.

My use case is that when going back in the history or forward in the history I have directional animations that occur when transitioning routes in an ajax application. I need to determine if the user is going backwards or forwards so the animations make sense. It's a shame that the popstate event doesn't support the direction of the event.

I'll also mention that my application is an AngularJS application in case there is a angular specific answer, although a more general purpose javascript solution would be optimal.

like image 240
nek4life Avatar asked May 17 '13 15:05

nek4life


1 Answers

I'm not sure which understanding is correct, but my understanding of HTML5 pushstate is different.

Push-state support simply allows you to capture changes in the browser's URL that would otherwise be sent as a request to a server (yours or someone else's). The purpose isn't to give you "forward" and "back" events, but more like general "location change" events. Then it's your app's job to check the URL and figure out where the user is trying to go.

Think about it this way: What if the user clicked a link in your app that you wanted to handle with javascript? You would have some event handlers setup which would capture the click and manipulate your app in some way. So clicking "back" or "forward" is like clicking a link, but all you get is the URL that the user is trying to view -- there's no link to bind events to.

So how do you know what the user is trying to do? You could manage state using global variables or any other way you can come up with. If you wanted to reduce code duplication, you could handle all of your app routing using URLs. So your click handler wouldn't bind to a particular link (or set of links), instead you could capture changes in the browser's URL and then determine how to handle the new URL.

BackboneJS does this using a Router object where specific paths are tied to specific router functions, which set the state of the app a particular way, for example:

MyAppRouter = Backbone.Router.extend({
    routes: {
        'home': 'setupHomeScreen',
        'recipes': 'setupRecipesList',
        'recipes/:id': 'setupRecipeScreen'
    },

    setupHomeScreen: function() {
        // ...
    },

    setupRecipesList: function() {
        // ...
    },

    setupRecipeScreen: function(id) {
        // ...
    },

    // ...

});

Please excuse the Backbone code on an Angular question. I'm still learning The Angular Way, and coming from a Backbone background that formed my understanding of pushstate.

To answer your question

If your views form some kind of hierarchy or order, you could store that in a global variable. Maybe you decide to come up with IDs for each view, and then every time the browser state changes, you push those IDs onto an array.

var viewHistory = [];

// ... they visited the recipe list. push it into the history
viewHistory.push('recipeList');

// ... they visited a particular recipe. push it into the history
viewHistory.push('recipe:13');

// ... they clicked the "back" button. we know from the URL that they want
// the recipeList, but we don't know if they're trying to go forward or back.
var nextView = 'recipeList';
if (viewHistory.indexOf(nextView) > 0) {
    // *** Back Button Clicked ***
    // this logic assumes that there is never a recipeList nested
    // under another recipeList in the view hierarchy
    animateBack(nextView);

    // don't forget to remove 'recipeList' from the history
    viewHistory.splice(viewHistory.indexOf(nextView), viewHistory.length);
} else {
    // *** They arrived some other way ***
    animateForward(nextView);
}
like image 74
colllin Avatar answered Sep 22 '22 11:09

colllin