如何使用angular检测浏览器后退按钮单击事件?

时间:2013-04-04 14:18:59

标签: javascript jquery angularjs

是否可以通过浏览器中的历史记录返回按钮检测用户是否输入了页面?我最好使用angular.js检测此动作。

我不想使用角度路由。如果用户提交表单并成功提交到服务器并进行重定向,它也应该有效,如果用户使用浏览器的后退按钮返回表单,也应该可以。

8 个答案:

答案 0 :(得分:116)

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

我正在使用一个运行块来启动它。首先我将实际位置存储在$ rootScope.actualLocation中,然后我听$ locationChangeSuccess,当它发生时我用新值更新actualLocation。

在$ rootScope中,我注意位置路径的变化,如果新位置等于previousLocation是因为没有触发$ locationChangeSuccess,这意味着用户已经使用了历史记录。

答案 1 :(得分:11)

如果您想要更精确的解决方案(检测后退和前进),我会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++;

        }

    }

 });

答案 2 :(得分:8)

对于ui-routing,我使用以下代码。

App.run()内,使用

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

如果你想要的话,你可以在'$ stateChangeSuccess'事件中得到'previousState'值,如下所示。

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

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

答案 3 :(得分:1)

我知道这是一个老问题。无论如何:)

贝玛的回答看起来很棒。但是,如果你想让它与'普通'网址一起使用(我猜没有哈希),你可以比较绝对路径,而不是$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?');
        }
    });
});

答案 4 :(得分:0)

JavaScript有一个本机历史对象。

window.history

查看MDN以获取更多信息; https://developer.mozilla.org/en-US/docs/DOM/window.history?redirectlocale=en-US&redirectslug=window.history

不确定多浏览器支持有多好。

<强>更新

似乎我上面所说的仅适用于Gecko类型的浏览器。

对于其他浏览器,请尝试使用history.js; https://github.com/browserstate/history.js

答案 5 :(得分:0)

因此,我已经将@Bema的答案转录为TypeScript,这就是它的样子:

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

我们不必从$rootScope服务创建属性,我们只需在本地actualLocation上关闭“按位置更改成功”和“按位置更改”代码即可变量。从那里,您可以按照自己的意愿做任何事情,就像原始代码一样。就我而言,我将考虑广播事件,以便单个控制器可以执行其必须执行的任何操作,但是如果需要,您可以包括全局操作

非常感谢,我希望这对其他打字稿用户有所帮助。

答案 6 :(得分:-2)

我对这个问题的解决方案是

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

答案 7 :(得分:-7)

按下后退按钮时,角度仅触发 $ routeChangeStart 事件, $ locationChangeStart 未触发。