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


79

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

我不想使用角度布线。如果用户提交表单,并且在成功提交到服务器并进行重定向之后,它也应该起作用,如果用户使用浏览器的“后退”按钮返回到表单,也应该可行。

Answers:


120

这是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
谢谢!看起来很棒。明天必须详细检查。特别是我必须检查如果更改了位置而不使用角度路线,但使用了“普通”超链接,是否也更新了$ rootScope.actualLocation。您认为使用普通超链接也可以使用吗?
Thomas Kremmel 2013年

4
是的。以前从未使用过角度路线,因此在您的示例中似乎没有正常的和不正常的(角度路线类型)超链接。所以就忘了我的意见..
托马斯Kremmel

2
$ locationProvider.html5Mode(true)仅用于使链接在jsFiddle中工作,您在项目中将不需要它。只需记住链接必须在#/之前,以便angular可以捕获它们(在小提琴中则不是)。如果您使用“普通”链接,例如/ book,angular不会捕获它,并且页面将被重定向到该url,这将重置您的应用程序,因为所有脚本都将被重新加载,这就是为什么它不起作用的原因。
Bertrand

19
对于任何不了解它如何工作的人(例如我):以常规方式更改网址(不使用后退/前进按钮)时,则回调$locationChangeSuccess触发事件。但是,当使用后退/前进按钮或api更改网址时,则会回调之前触发事件。因此,当watch回调触发时已经等于。正是角度内部如何工作,此解决方案仅使用了这种内部行为。 $watchhistory.back()$locationChangeSuccess $watchactualLocationnewLocation
Ruslan Stelmachenko

5
上面的代码无法检测到URL查询字符串的更改,例如从/#/news/?page=2/#/news/?page=3。为了捕捉这些,您可以使用$location.absUrl()代替$location.path()(在上面使用的两个地方)。
迈克尔”

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

        }

    }

 });

8

对于ui路由,我使用以下代码。

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

这不会检测到后退按钮的点击-只会检测到用户是在两个状态名称之间切换-通过后退按钮或在两个页面之间单击。同样,这也不考虑状态的参数。
艾米B

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

0

JavaScript具有本机历史对象。

window.history

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

不知道它在多浏览器支持上的表现如何。

更新资料

看来我上面所说的仅适用于Gecko型浏览器。

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


谢谢你的评论。因为我更喜欢以角度方式进行操作,所以我将尝试给Bertrands一个答案。
Thomas Kremmel 2013年

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服务之外创建属性,我们只需使“ on location change success”(成功更改位置)和“ on location changes”(更改位置)代码都靠近局部actualLocation变量即可。从那里,您可以按照自己的意愿做任何事情,就像原始代码一样。就我而言,我会考虑在播放事件使个别控制器可以为所欲为必须的,但你可以包括全球行动,如果你要

感谢您的出色回答,希望对其他打字稿用户有所帮助。


-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

当按下后退按钮时,角度仅触发$ routeChangeStart事件,而不会触发$ locationChangeStart事件。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.