How to detect browser back button click event using angular?

The question:

Is it possible to detect that a user entered a page through using the history back button in his browser? Preferably I want to detect this action using angular.js.

I do not want to use angular routing. It should also work if a user submits a form and after a successful submit to the server and a re-direct, it should also be possible if the user goes back to the form using the back button of the browser.

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

Here is a solution with Angular.

myApp.run(function($rootScope, $route, $location){
   //Bind the `$locationChangeSuccess` event on the rootScope, so that we dont need to 
   //bind in induvidual controllers.

   $rootScope.$on('$locationChangeSuccess', function() {
        $rootScope.actualLocation = $location.path();
    });        

   $rootScope.$watch(function () {return $location.path()}, function (newLocation, oldLocation) {
        if($rootScope.actualLocation === newLocation) {
            alert('Why did you use history back?');
        }
    });
});

I am using a run block to kickstart this. First I store the actual location in $rootScope.actualLocation, then I listen to $locationChangeSuccess and when it happens I update actualLocation with the new value.

In the $rootScope I watch for changes in the location path and if the new location is equal to previousLocation is because the $locationChangeSuccess was not fired, meaning the user has used the history back.

Method 2

If you want more precise solution (detecting back and forward) I extended solution delivered by Bertrand:

$rootScope.$on('$locationChangeSuccess', function() {
    $rootScope.actualLocation = $location.path();
});


$rootScope.$watch(function () {return $location.path()}, function (newLocation, oldLocation) {

    //true only for onPopState
    if($rootScope.actualLocation === newLocation) {

        var back,
            historyState = $window.history.state;

        back = !!(historyState && historyState.position <= $rootScope.stackPosition);

        if (back) {
            //back button
            $rootScope.stackPosition--;
        } else {
            //forward button
            $rootScope.stackPosition++;
        }

    } else {
        //normal-way change of page (via link click)

        if ($route.current) {

            $window.history.replaceState({
                position: $rootScope.stackPosition
            });

            $rootScope.stackPosition++;

        }

    }

 });

Method 3

For ui-routing, I’m using the below code.

Inside App.run(), use

 $rootScope.$on("$stateChangeStart", function (event, toState, toParams, fromState, fromParams) 
 {

    if (toState.name === $rootScope.previousState )
       { 
          // u can any 1 or all of below 3 statements
         event.preventDefault();  // to Disable the event
         $state.go('someDefaultState'); //As some popular banking sites are using 
         alert("Back button Clicked");

       }
 else
      $rootScope.previousState= fromState.name;
   });

You can get the ‘previousState’ value in ‘$stateChangeSuccess’ event also like as follows if `you want.

    $rootScope.$on("$stateChangeSuccess", function (event, toState, toParams, fromState, fromParams) 
   {

        $rootScope.previousStatus = fromState.name;
    });

Method 4

I know it is an old question. Anyways 🙂

Bema’s answer looks great. However, if you want to make it work with ‘normal’ urls (without hash I guess), you could compare absolute path, instead of the one returned by $location.path():

myApp.run(function($rootScope, $route, $location){
//Bind the `$locationChangeSuccess` event on the rootScope, so that we dont need to 
//bind in induvidual controllers.

    $rootScope.$on('$locationChangeSuccess', function() {
        $rootScope.actualLocation = $location.absUrl();
    });      

    $rootScope.$watch(function () {return $location.absUrl()}, function (newLocation, oldLocation) {
        if($rootScope.actualLocation === newLocation) {
            alert('Why did you use history back?');
        }
    });
});

Method 5

JavaScript has a native history object.

window.history

Check the MDN for more info; https://developer.mozilla.org/en-US/docs/DOM/window.history?redirectlocale=en-US&redirectslug=window.history

Not sure how good it’s on multi-browser support tho.

Update

Seems what I’ve called above is only for Gecko-type browsers.

For other browsers try to use history.js; https://github.com/browserstate/history.js

Method 6

So, I’ve transcribed the answer by @Bema into TypeScript, and this is what it looks like:

namespace MyAwesomeApp {
    // ReSharper disable once Class
    function detectBackButton(
        $rootScope: ng.IRootScopeService,
        $location: ng.ILocationService
    ) {
        let actualLocation: string = '';

        $rootScope.$on('$locationChangeSuccess',
            () => {
                actualLocation = $location.path();
            });

        $rootScope.$watch(() => $location.path(),
            (newLocation: string, oldLocation: string) => {
                if (actualLocation === newLocation) {
                    //$rootScope.$broadcast('onBackButtonPressed', null);
                }
            });
    }

    detectBackButton.$inject = [
        '$rootScope',
        '$location'
    ];

    angular
        .module('app')
        .run(detectBackButton);
}

We don’t have to create a property off of the $rootScope service, we can just have our ‘on location change success’ and ‘on location changed’ code both close over the local actualLocation variable. From there, you can do whatever you like, just as in the original code. For my part, I’d consider broadcasting an event so that individual controllers can do whatever they must, but you could include global actions if you had to.

Thanks for the great answer, and I hope this helps other typescript users out there.

Method 7

My Solutio to this problem is

app.run(function($rootScope, $location) {
    $rootScope.$on('$locationChangeSuccess', function() {
        if($rootScope.previousLocation == $location.path()) {
            console.log("Back Button Pressed");
        }
        $rootScope.previousLocation = $rootScope.actualLocation;
        $rootScope.actualLocation = $location.path();
    });
});

Method 8

when pressing back button, angular fires only $routeChangeStart event, $locationChangeStart not fired.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment