【问题标题】:How to detect browser back button click event using angular?如何使用角度检测浏览器后退按钮单击事件?
【发布时间】:2013-03-26 16:24:03
【问题描述】:

是否可以检测到用户通过浏览器中的历史返回按钮进入了一个页面?最好我想使用 angular.js 来检测这个动作。

我不想使用角度路由。如果用户提交表单并且在成功提交到服务器并重定向后,它也应该工作,如果用户使用浏览器的后退按钮返回表单也应该是可能的。

【问题讨论】:

    标签: javascript jquery angularjs


    【解决方案1】:

    所以,我已经将@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 变量上。从那里,您可以做任何您喜欢的事情,就像在原始代码中一样。就我而言,我会考虑广播一个事件,以便各个控制器可以做他们必须做的任何事情,但你可以包括全局操作如果你不得不

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

    【讨论】:

      【解决方案2】:

      这是一个使用 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,当它发生时,我使用新值更新实际位置。

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

      【讨论】:

      • 谢谢!看起来很棒。明天一定要详细检查。特别是我必须检查 $rootScope.actualLocation 如果位置更改而不使用角度路线,但“正常”超链接是否也更新。你认为它也可以使用普通的超链接吗?
      • 啊是的。以前从未使用过角度路线,因此查看您的示例,似乎没有正常和不正常(角度路线类型)超链接。所以忘记我的评论吧..
      • $locationProvider.html5Mode(true) 仅用于使链接在 jsFiddle 中工作,您的项目中不需要它。请记住,链接需要以 #/ 开头,以便 Angular 可以捕获它们(在小提琴中它们不是)。如果您使用“普通”链接,例如 /book,angular 不会捕获它,并且页面将被重定向到该 url,这将重置您的应用程序,因为所有脚本都将被重新加载,这就是它不起作用的原因.
      • 对于不了解其工作原理的任何人(如我):当 url 以通常方式更改时(不使用后退/前进按钮),然后 $locationChangeSuccess 事件触发 之后 $watch 回调。但是当使用后退/前进按钮或history.back() api 更改 url 时,$locationChangeSuccess 事件触发 before $watch 回调。所以当观察回调触发actualLocation已经等于newLocation。这就是 Angular 在内部的工作方式,而这个解决方案只是使用这种内部行为。
      • 上面的代码没有检测到 URL 查询字符串的变化,比如从 /#/news/?page=2/#/news/?page=3。你也可以使用$location.absUrl() 而不是$location.path()(在上面使用它的两个地方)来捕捉它们。
      【解决方案3】:

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

      【讨论】:

      • 这不会检测后退按钮的点击——这只是检测用户是否在两个状态名称之间切换——无论是通过后退按钮,还是通过在两个页面之间单击。这也不看状态的参数。
      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          【解决方案6】:

          如果您想要更精确的解决方案(前后检测),我扩展了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++;
          
                  }
          
              }
          
           });
          

          【讨论】:

            【解决方案7】:

            当按下返回按钮时,angular 只会触发 $routeChangeStart 事件,$locationChangeStart 不会触发。

            【讨论】:

              【解决方案8】:

              JavaScript 有一个原生的历史对象。

              window.history
              

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

              不确定它在多浏览器支持方面有多好。

              更新

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

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

              【讨论】:

              • 感谢您的评论。由于我更喜欢​​有角度的方法,所以我会尝试一下 Bertrands 的回答。
              猜你喜欢
              • 2017-05-08
              • 2014-11-06
              • 2014-10-30
              • 1970-01-01
              • 2011-09-15
              • 2022-10-25
              相关资源
              最近更新 更多