更改路线不会滚动到新页面的顶部


271

至少对于我来说,我发现路线改变时有一些不希望有的行为。在教程http://angular.github.io/angular-phonecat/step-11/app/#/phones的步骤11中, 您可以看到电话列表。如果滚动到底部并单击最新滚动条之一,则可以看到滚动条不在顶部,而是在中间。

我也在我的一个应用程序中找到了它,我想知道如何才能滚动到顶部。我可以手动操作,但是我认为应该有其他我不知道的优雅方法。

那么,当路线改变时,有没有一种优雅的方式可以滚动到顶部?

Answers:


578

问题是您的ngView加载新视图时会保留滚动位置。您可以指示$anchorScroll“在视图更新后滚动视口”(文档有点含糊,但是在此处滚动意味着滚动到新视图的顶部)。

解决方案是将添加autoscroll="true"到您的ngView元素:

<div class="ng-view" autoscroll="true"></div>

1
对于我来说,这可以正常工作,页面可以很好地滚动到顶部,但是我正在使用带有scrollScroll指令的scrollTo,是否有办法将平滑滚动到顶部?另外,您的解决方案也可以滚动到视图顶部而不是页面顶部页面顶部?
xzegga 2014年

19
docs状态如果属性设置为不带值,请启用scrolling。因此,您可以将代码缩短为公正,<div class="ng-view" autoscroll></div>并且其工作原理相同(除非您希望使滚动成为有条件的)。
Daniel Liuzzi 2014年

7
它对我不起作用,文档没有说它将滚动到顶部
Pencilcheck 2015年

6
我发现如果将autoscroll设置为true,则当用户“返回”时,他们将返回页面顶部,而不是返回页面顶部,这是不希望的行为。
axzr 2015年

4
这会将您的视图滚动到该div。因此,仅当碰巧在页面顶部时,它才会滚动到页面顶部。否则,您将必须$window.scrollTo(0,0)在$ stateChangeSuccess事件侦听器中运行
Filip Sobczak 2015年

46

只需将这段代码运行

$rootScope.$on("$routeChangeSuccess", function (event, currentRoute, previousRoute) {

    window.scrollTo(0, 0);

});

6
$window.scrollTop(0,0)对我来说比自动滚动更好。就我而言,我具有随过渡进入和离开的视图。
IsidroGH 2014年

1
这对我来说比autoscroll =“ true”更好。自动滚动由于某些原因而来不及,它将在新视图可见后滚动到顶部。
Gregory Bolkenstijn 2015年

5
这对我来说最有效:$ rootScope。$ on('$ stateChangeSuccess',function(){$(“ html,body”)。animate({scrollTop:0},200);});
James Gentes

$ window.scrollTop应该是$ window.scrollTo,否则它说它不存在。这个解决方案很棒!
软件先知

@JamesGentes我也需要这个。谢谢。
Mackenzie Browne's

36

这段代码对我来说很棒。.我希望它对您也很棒。.您要做的就是将$ anchorScroll注入到您的运行块中,并将监听器函数应用于rootScope,就像我在下面的示例中所做的..

 angular.module('myAngularApp')
.run(function($rootScope, Auth, $state, $anchorScroll){
    $rootScope.$on("$locationChangeSuccess", function(){
        $anchorScroll();
    });

这是Angularjs模块的调用顺序:

  1. app.config()
  2. app.run()
  3. directive's compile functions (if they are found in the dom)
  4. app.controller()
  5. directive's link functions (again, if found)

RUN BLOCK 在创建注射器后被执行,并用于启动应用程序。这意味着当您重定向到新的路由视图时,run块中的侦听器将调用

$ anchorScroll()

现在您可以看到滚动从新的路由视图开始到顶部:)


最后,一种适用于粘性标题的解决方案!谢谢!
法比奥

31

尝试的每个组合一两个小时后ui-view autoscroll=true$stateChangeStart$locationChangeStart$uiViewScrollProvider.useAnchorScroll()$provide('$uiViewScroll', ...),和许多人一样,预期我无法滚动到顶部上新页面的工作。

最终这对我有用。它捕获pushState和replaceState,并且仅在导航到新页面时更新滚动位置(后退/前进按钮保留其滚动位置):

.run(function($anchorScroll, $window) {
  // hack to scroll to top when navigating to new URLS but not back/forward
  var wrap = function(method) {
    var orig = $window.window.history[method];
    $window.window.history[method] = function() {
      var retval = orig.apply(this, Array.prototype.slice.call(arguments));
      $anchorScroll();
      return retval;
    };
  };
  wrap('pushState');
  wrap('replaceState');
})

这对我的用例非常有用。我正在使用带有嵌套视图(深几个级别)的Angular UI-Router。谢谢!
Joshua Powell

仅供参考-您需要在角度为HTML5模式下才能使用pushState:$locationProvider.html5Mode(true); @wkronkel有没有在角度模式下不使用HTML5模式时实现此目的的方法?由于html 5模式处于活动状态,导致页面重新加载
引发

@britztopher您可以加入内置事件并维护自己的历史,对吗?
Casey

2
您将此附加.run到哪里?有角度的app对象?
Philll_t

1
@Philll_t:是的,该run函数是每个角度模块对象上可用的方法。
辛里奇

18

这是我(看似)健壮,完整和(相当)简洁的解决方案。它使用最小化兼容样式(以及对模块的angular.module(NAME)访问)。

angular.module('yourModuleName').run(["$rootScope", "$anchorScroll" , function ($rootScope, $anchorScroll) {
    $rootScope.$on("$locationChangeSuccess", function() {
                $anchorScroll();
    });
}]);

PS我发现,无论将autoscroll设置为true还是false,都不会起作用。


1
嗯..这首先将视图滚动到顶部,然后视图在屏幕上为我改变。
Strelok

我一直在寻找干净的解决方案。如果您单击“后退”,它将带您到上次停止的地方-对于某些人来说可能不希望这样做,但是我喜欢这种情况。
mjoyce91 '16

15

使用angularjs UI路由器,我正在做的是:

    .state('myState', {
        url: '/myState',
        templateUrl: 'app/views/myState.html',
        onEnter: scrollContent
    })

带有:

var scrollContent = function() {
    // Your favorite scroll method here
};

它永远不会在任何页面上失败,并且它不是全局的。


1
好主意,您可以使用以下方法使它更短:onEnter: scrollContent
zucker 2015年

2
你把什么写在哪里// Your favorite scroll method here
特工Zebra

4
好的时机:我在原始代码中此注释的上方两行,尝试使用ui-router-title。我用 $('html, body').animate({ scrollTop: -10000 }, 100);
莱昂佩尔蒂埃

1
哈哈,太好了!奇怪的是,它并不总是对我有用。我有一些视图很短,没有滚动条,还有两个视图有滚动条,当我将onEnter: scrollContent它们放置在每个视图上时,它仅适用于第一个视图/路线,而不适用于第二个视图/路线。奇怪。
特工Zebra

1
我不确定,不,它只是在应该显示的时候不会滚动到顶部。当我在“路线”之间单击时,其中一些仍在滚动到ng-view所在的位置,而不是滚动页面的绝对顶部。那有意义吗?
特工斑马

13

对于遇到标题中描述的问题(与我一样)并且也使用AngularUI Router插件的任何人,请仅供参考...

正如在此SO问题中所要求和回答的那样,当您更改路线时,angular-ui路由器会跳至页面底部。
无法找出为什么页面底部加载?Angular UI-Router自动滚动问题

但是,正如答案所指出的,您可以通过autoscroll="false"在上说出来来关闭此行为ui-view

例如:

<div ui-view="pagecontent" autoscroll="false"></div>
<div ui-view="sidebar" autoscroll="false"></div> 

http://angular-ui.github.io/ui-router/site/#/api/ui.router.state.directive:ui-view


16
我的并没有跳到底部,它只是保持滚动位置而不是跳到顶部。
Stephen Smith

5
这不能回答问题。我遇到了同样的问题-路线更改时,滚动级别仅停留在同一位置,而不是滚动到顶部。我可以使它滚动到顶部,但是只能通过更改哈希值来实现,出于明显的原因,我不想这样做。
2014年

7

你可以使用这个JavaScript

$anchorScroll()

3
好吧,在angularjs中并不是那么简单。您的解决方案仅在jquery中有效。我正在寻找的是在angularjs中执行此操作的一种优雅方法
Matias Gonzalez

4
您是否尝试过使用$ anchorScroll(),它已记录在docs.angularjs.org/api/ng.%24anchorScroll中
CodeIsLife 2014年

1
仅供参考:如果您在默认前进/后退浏览器按钮$location.hash('top')之前指定的位置$anchorScroll()不再有效,他们不断将您重定向到URL中的哈希
Roy 2014年

7

如果您使用ui-router,则可以使用(运行时)

$rootScope.$on("$stateChangeSuccess", function (event, currentState, previousState) {
    $window.scrollTo(0, 0);
});

我知道这应该工作,我广泛使用“ $ stateChangeSuccess”,但是由于某些原因,$ window.scrollTo(0,0)不能正常工作。
Abhas 2015年

4

我找到了这个解决方案。如果转到新视图,该函数将被执行。

var app = angular.module('hoofdModule', ['ngRoute']);

    app.controller('indexController', function ($scope, $window) {
        $scope.$on('$viewContentLoaded', function () {
            $window.scrollTo(0, 0);
        });
    });

3

将autoScroll设置为true并不是我的招数,所以我选择了另一个解决方案。我构建了一个服务,该服务在每次路线更改时都挂钩,并且使用内置的$ anchorScroll服务滚动到顶部。为我工作:-)。

服务:

 (function() {
    "use strict";

    angular
        .module("mymodule")
        .factory("pageSwitch", pageSwitch);

    pageSwitch.$inject = ["$rootScope", "$anchorScroll"];

    function pageSwitch($rootScope, $anchorScroll) {
        var registerListener = _.once(function() {
            $rootScope.$on("$locationChangeSuccess", scrollToTop);
        });

        return {
            registerListener: registerListener
        };

        function scrollToTop() {
            $anchorScroll();
        }
    }
}());

注册:

angular.module("mymodule").run(["pageSwitch", function (pageSwitch) {
    pageSwitch.registerListener();
}]);

3

派对晚了一点,但是我尝试了所有可能的方法,但是没有任何正确的方法。这是我的优雅解决方案:

我使用一个控制器来控制ui-router的所有页面。它允许我将未经身份验证或验证的用户重定向到适当的位置。大多数人将中间件放在其应用程序的配置中,但是我需要一些http请求,因此全局控制器对我来说更好。

我的index.html看起来像:

<main ng-controller="InitCtrl">
    <nav id="top-nav"></nav>
    <div ui-view></div>
</main>

我的initCtrl.js看起来像:

angular.module('MyApp').controller('InitCtrl', function($rootScope, $timeout, $anchorScroll) {
    $rootScope.$on('$locationChangeStart', function(event, next, current){
        // middleware
    });
    $rootScope.$on("$locationChangeSuccess", function(){
        $timeout(function() {
            $anchorScroll('top-nav');
       });
    });
});

我尝试了所有可能的方法,这种方法效果最好。


1
对于我来说,这是唯一使用角形材料的工具,将其id="top-nav"放置在正确的元素上也很重要。
BatteryAcid

2

上面的所有答案都破坏了预期的浏览器行为。大多数人想要的是在“新”页面中会滚动到顶部的内容,但是如果您通过“后退”(或“前进”)按钮到达那里,则会返回到上一个位置。

如果您采用HTML5模式,那么这很容易(尽管我敢肯定有些聪明的人可以弄清楚如何使它更加优雅!):

// Called when browser back/forward used
window.onpopstate = function() { 
    $timeout.cancel(doc_scrolling); 
};

// Called after ui-router changes state (but sadly before onpopstate)
$scope.$on('$stateChangeSuccess', function() {
    doc_scrolling = $timeout( scroll_top, 50 );

// Moves entire browser window to top
scroll_top = function() {
    document.body.scrollTop = document.documentElement.scrollTop = 0;
}

它的工作方式是路由器假定它将滚动到顶部,但会延迟一点时间,使浏览器有机会完成。如果浏览器然后通知我们更改是由于后退/前进导航引起的,它将取消超时,并且永远不会发生滚动。

我使用原始document命令进行滚动,因为我想移至窗口的整个顶部。如果只想ui-view滚动,则使用上述技术设置autoscroll="my_var"控制位置my_var。但是我认为,如果您以“新”身份进入页面,大多数人都希望滚动整个页面。

上面使用了ui-router,尽管您可以通过替换$routeChangeSuccess为ng-route来使用$stateChangeSuccess


您如何实施呢?您会将它放在常规的angular-ui路由代码中,例如以下代码吗?var routerApp = angular.module('routerApp', ['ui.router']); routerApp.config(function($stateProvider, $urlRouterProvider, $locationProvider) { $urlRouterProvider.otherwise('/home'); $locationProvider.html5Mode(false).hashPrefix(""); $stateProvider // HOME STATES AND NESTED VIEWS ======================================== .state('home', { url: '/home', templateUrl: 'partial-home.html' }) });
特工斑马

嗯...没有用:-(它只是杀死了应用程序。 .state('web', { url: '/webdev', templateUrl: 'partial-web.html', controller: function($scope) { $scope.clients = ['client1', 'client2', 'client3']; // I put it here } }) 我只是在学习这些东西,也许我缺少了一些东西:D
Zebra特工

@AgentZebra至少,控制器将需要以$ timeout作为输入(因为我的代码引用了它),但也许更重要的是,我提到的控制器需要处于高于单个状态的水平(您需要可能会将这些语句包含在您的顶级控制器中)。最初的AngularJS学习曲线确实非常困难。祝好运!
Dev93

2

提供的答案均未解决我的问题。我在视图之间使用动画,并且滚动总是在动画之后进行。我找到的解决方案是使滚动到顶部发生在动画之前是以下指令:

yourModule.directive('scrollToTopBeforeAnimation', ['$animate', function ($animate) {
    return {
        restrict: 'A',
        link: function ($scope, element) {
            $animate.on('enter', element, function (element, phase) {

                if (phase === 'start') {

                    window.scrollTo(0, 0);
                }

            })
        }
    };
}]);

我将其插入到我的视图中,如下所示:

<div scroll-to-top-before-animation>
    <div ng-view class="view-animation"></div>
</div>

2

简单解决方案,添加scrollPositionRestoration启用的主路由模块。
像这样:

const routes: Routes = [

 {
   path: 'registration',
   loadChildren: () => RegistrationModule
},
];

 @NgModule({
  imports: [
   RouterModule.forRoot(routes,{scrollPositionRestoration:'enabled'})
  ],
 exports: [
 RouterModule
 ]
 })
  export class AppRoutingModule { }


0

我终于得到了我所需要的。

我需要滚动到顶部,但要进行一些过渡而不是

您可以在逐个路由级别进行控制。
我将上述解决方案通过@wkonkel组合在一起,并noScroll: true在一些路由声明中添加了一个简单的参数。然后我在过渡中抓住了这一点。

总而言之:在新转换时,它浮动到页面顶部,在Forward/ Back转换时它不浮动到页面顶部,并且允许您在必要时覆盖此行为。

代码:(以前的解决方案加上一个额外的noScroll选项)

  // hack to scroll to top when navigating to new URLS but not back/forward
  let wrap = function(method) {
    let orig = $window.window.history[method];
    $window.window.history[method] = function() {
      let retval = orig.apply(this, Array.prototype.slice.call(arguments));
      if($state.current && $state.current.noScroll) {
        return retval;
      }
      $anchorScroll();
      return retval;
    };
  };
  wrap('pushState');
  wrap('replaceState');

把它放在你的app.run块并注入$state...myApp.run(function($state){...})

然后,如果您不想滚动到页面顶部,请创建如下路线:

.state('someState', {
  parent: 'someParent',
  url: 'someUrl',
  noScroll : true // Notice this parameter here!
})

0

这对我有用,包括自动滚动

<div class="ngView" autoscroll="true" >

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.