ルートを変更しても新しいページの一番上にスクロールしない


271

ルートが変更されたときに、少なくとも私にとって望ましくない動作がいくつか見つかりました。チュートリアルhttp://angular.github.io/angular-phonecat/step-11/app/#/phonesのステップ11で、 電話のリストを確認できます。一番下までスクロールして最新のものをクリックすると、スクロールが一番上ではなく、真ん中にあることがわかります。

私は自分のアプリの1つでもこれを見つけました。どうすればこれを一番上までスクロールできますか。私はそれを手動で行うことができますが、これを行うには私にはわからない他のエレガントな方法があるはずだと思います。

では、ルートが変わったときに上にスクロールするエレガントな方法はありますか?

回答:


578

問題は、ngViewが新しいビューをロードするときにスクロール位置を保持することです。$anchorScroll「ビューの更新後にビューポートをスクロールする」ように指示できます(ドキュメントは少しあいまいですが、ここでスクロールすると、新しいビューの上部までスクロールすることになります)。

解決策autoscroll="true"ngView要素に追加することです:

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

1
これは私にとってはうまくいきますが、ページは上にうまくスクロールしますが、smoothScrollディレクティブでscrollToを使用しています。上にスクロールする方法はありますか?さらに、ソリューションはページではなくビューの一番上にスクロールしますページのトップ?
xzegga

19
ドキュメントの状態属性が値なしで設定されている場合は、スクロールを有効にします。したがって、コードを短くして<div class="ng-view" autoscroll></div>、同じように機能するようにすることができます(スクロールを条件付きにする場合を除きます)。
Daniel Liuzzi 14

7
それは私にとっては機能しません、ドキュメントはそれが一番上にスクロールすることを言っていません
Pencilcheck

6
autoscrollがtrueに設定されている場合、ユーザーが「戻る」と、ページの先頭ではなく、ページの先頭に戻ることがわかりますが、これは望ましくない動作です。
axzr 2015年

4
これにより、ビューがこのdivまでスクロールされます。したがって、ページの上部にある場合にのみ、ページの上部にスクロールします。それ以外の場合は$window.scrollTo(0,0)、$ stateChangeSuccessイベントリスナーで実行する必要があります
Filip Sobczak

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、2015

$ window.scrollTopは$ window.scrollToである必要があります。そうでない場合は存在しないと表示されます。このソリューションはうまく機能します!
ソフトウェアの預言​​者2016

@JamesGentes私もこれが必要でした。ありがとう。
マッケンジーブラウン2017年

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

これで、新しいルーテッドビューが表示され、スクロールが一番上に表示されます:)


最後に、スティッキーヘッダーで機能するソリューションです。ありがとうございました!
Fabio

31

のすべての組み合わせを試すの時間か2の後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、

FYI-pushStateを使用するには、角度でHTML5モードにする必要があります。$locationProvider.html5Mode(true); @ wkronkelは、角度でHTML5モードを使用しない場合にこれを達成する方法はありますか?html 5モードがアクティブになっているため、ページの再読み込みで404エラーがスローされる
britztopher

@britztopher組み込みのイベントにフックして、自分の履歴を維持できますよね?
ケーシー

2
これ.runをどこに追加しますか?あなたのappオブジェクトの角度?
Philll_t 2016

1
@Philll_t:はい、run関数はすべての角度モジュールオブジェクトで使用できるメソッドです。
Hinrich

18

これが私の(一見)堅牢で完全かつ(かなり)簡潔なソリューションです。ミニファイ互換スタイル(およびモジュールへのangular.module(NAME)アクセス)を使用します。

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

PS私は、自動スクロール機能がtrueまたはfalseに設定されているかどうかに影響を与えないことを発見しました。


1
うーん..これは最初にビューを上にスクロールし、次にビューが画面上で変わります。
Strelok、2015

私が探していたクリーンなソリューション。「戻る」をクリックすると、中断したところに移動します。これは一部のユーザーには望ましくない場合がありますが、私の場合は気に入っています。
mjoyce91

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
ザッカーを

2
あなたが書いたところには何を入れます// Your favorite scroll method hereか?
エージェントゼブラ2015年

4
良いタイミング:元のコードでは、このコメントの2行上で、ui-router-titleを試しました。私が使う $('html, body').animate({ scrollTop: -10000 }, 100);
レオン・ペルティエ

1
はは、すごい!奇妙なことに、それはいつも私にはうまくいきません。スクロールがない短いビューとスクロールがある2つのビューがあります。onEnter: scrollContent各ビューに配置すると、最初のビュー/ルートでのみ機能し、2番目のビューでは機能しません。奇妙な。
エージェントゼブラ

1
わからない、いいえ、必要なときに上にスクロールしないだけです。「ルート」の間をクリックしても、一部のページは、囲んでいるページの最上部ではなく、ng-viewのある場所までスクロールしています。それは理にかなっていますか?
エージェントゼブラ2015年

13

タイトルに記載されている問題に遭遇した(私がしたように)AngularUIルーターも使用している人のための参考情報プラグイン ...

この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
鉱山は下にジャンプせず、上に移動するのではなく、スクロール位置を保持します。
スティーブンスミス

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

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 2017年

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-ルータは、あなたものの代わりにスワップすることによりNG-ルートを使用することができます$routeChangeSuccessため$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' }) });
エージェントゼブラ2015年

うーん...うまく.state('web', { url: '/webdev', templateUrl: 'partial-web.html', controller: function($scope) { $scope.clients = ['client1', 'client2', 'client3']; // I put it here } }) いきませんでした:-(それは単にアプリを殺します。 私はこのことを学んでいるだけで、おそらく何か不足しています:D
エージェントゼブラ

@AgentZebra少なくとも、コントローラーは$ timeoutを入力として受け取る必要があります(私のコードがそれを参照しているため)。しかし、おそらくもっと重要なこととして、私が言及しているコントローラーは、個々の状態より上のレベルに存在する必要があります(トップレベルのコントローラーにステートメントを含めることができます)。AngularJSの最初の学習曲線は確かに非常に困難です。幸運を!
Dev93、2015年

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" >

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.