How to detect browser back button click event using angular?

JavascriptJqueryAngularjs

Javascript Problem Overview


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.

Javascript Solutions


Solution 1 - Javascript

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.

Solution 2 - Javascript

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++;

        }

    }

 });

Solution 3 - Javascript

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;
    });

Solution 4 - Javascript

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?');
        }
    });
});

Solution 5 - Javascript

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

Solution 6 - Javascript

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.

Solution 7 - Javascript

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();
	});
});

Solution 8 - Javascript

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionThomas KremmelView Question on Stackoverflow
Solution 1 - JavascriptBertrandView Answer on Stackoverflow
Solution 2 - JavascriptMariuszView Answer on Stackoverflow
Solution 3 - Javascriptharish polankiView Answer on Stackoverflow
Solution 4 - JavascriptJahongir RahmonovView Answer on Stackoverflow
Solution 5 - Javascriptuser1467267View Answer on Stackoverflow
Solution 6 - JavascriptAndrew GrayView Answer on Stackoverflow
Solution 7 - Javascriptsyraz37View Answer on Stackoverflow
Solution 8 - JavascriptbullgareView Answer on Stackoverflow