Thay đổi tuyến đường không cuộn lên đầu trong trang mới


271

Tôi đã tìm thấy một số hành vi không mong muốn, ít nhất là đối với tôi, khi tuyến đường thay đổi. Trong bước 11 của hướng dẫn http://angular.github.io/angular-phonecat/step-11/app/#/phones bạn có thể xem danh sách điện thoại. Nếu bạn cuộn xuống phía dưới và nhấp vào một trong những cái mới nhất, bạn có thể thấy rằng cuộn không ở trên cùng, thay vào đó là loại ở giữa.

Tôi cũng đã tìm thấy điều này trong một trong những ứng dụng của mình và tôi đã tự hỏi làm thế nào tôi có thể đưa nó lên đầu trang. Tôi có thể làm điều đó một cách thủ công, nhưng tôi nghĩ rằng nên có cách thanh lịch khác để làm điều này mà tôi không biết.

Vì vậy, có một cách thanh lịch để cuộn lên đầu khi tuyến đường thay đổi?

Câu trả lời:


578

Vấn đề là ngView của bạn giữ được vị trí cuộn khi tải chế độ xem mới. Bạn có thể hướng dẫn $anchorScroll"cuộn chế độ xem sau khi chế độ xem được cập nhật" (các tài liệu hơi mơ hồ, nhưng cuộn ở đây có nghĩa là cuộn lên trên cùng của chế độ xem mới).

Giải pháp là thêm autoscroll="true"vào phần tử ngView của bạn:

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

1
Công việc này đối với tôi, cuộn trang tốt lên đầu, nhưng tôi đang sử dụng scrollTo với chỉ thị smoothScroll, có cách nào để cuộn smoth lên trên không?, Ngoài ra, cuộn giải pháp của bạn lên trên cùng của trang không có để cuộn đầu trang?
xzegga

19
Trạng thái tài liệu Nếu thuộc tính được đặt không có giá trị, hãy bật cuộn . Vì vậy, bạn có thể rút ngắn mã thành chỉ <div class="ng-view" autoscroll></div>và nó hoạt động như nhau (trừ khi bạn muốn thực hiện cuộn có điều kiện).
Daniel Liuzzi

7
nó không hoạt động với tôi, tài liệu không nói rằng nó sẽ cuộn lên trên cùng
Pencilcheck

6
Tôi thấy rằng nếu autoscroll được đặt thành true thì khi người dùng 'quay lại', họ sẽ quay lại đầu trang chứ không phải nơi họ rời khỏi đó, đó là hành vi không mong muốn.
axzr

4
Điều này sẽ cuộn quan điểm của bạn đến div này. Vì vậy, nó sẽ cuộn lên đầu trang chỉ khi nó ở đầu trang. Nếu không, bạn sẽ phải chạy $window.scrollTo(0,0)trong trình nghe sự kiện $ stateChangeSuccess
Filip Sobczak

46

Chỉ cần đặt mã này để chạy

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

    window.scrollTo(0, 0);

});

6
$window.scrollTop(0,0)làm việc tốt hơn cho tôi hơn autoscroll. Trong trường hợp của tôi, tôi có các khung nhìn nhập và rời với các hiệu ứng chuyển tiếp.
IsidroGH

1
Điều này cũng hoạt động tốt hơn đối với tôi so với autoscroll = "true". Autoscroll đã quá muộn vì một số lý do, nó sẽ cuộn lên trên cùng sau khi chế độ xem mới đã hiển thị.
Gregory Bolkenstijn

5
Điều này hoạt động tốt nhất với tôi: $ rootScope. $ On ('$ stateChangeSuccess', function () {$ ("html, body"). Animate ({scrollTop: 0}, 200);});
James Gentes

$ window.scrollTop phải là $ window.scrollTo, nếu không nó nói nó không tồn tại. Giải pháp này hoạt động rất tốt!
Tiên tri phần mềm

@JamesGentes tôi cũng cần cái này Cảm ơn.
Mackenzie Browne

36

Mã này đã làm việc rất tốt cho tôi .. Tôi hy vọng nó cũng sẽ làm việc tốt cho bạn .. Tất cả những gì bạn phải làm chỉ là tiêm $ anchorScroll vào khối chạy của bạn và áp dụng chức năng nghe cho rootScope như tôi đã làm trong ví dụ dưới đây ..

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

Đây là thứ tự gọi của Mô-đun 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 được thực thi sau khi trình tạo được tạo và được sử dụng để khởi động ứng dụng .. có nghĩa là khi bạn chuyển hướng đến chế độ xem tuyến đường mới, trình nghe trong khối chạy sẽ gọi

$ anchorScroll ()

và bây giờ bạn có thể thấy cuộn bắt đầu lên trên cùng với chế độ xem được định tuyến mới :)


Cuối cùng, một giải pháp đang làm việc với các tiêu đề dính! Cảm ơn bạn!
Fabio

31

Sau một hoặc hai giờ cố gắng mỗi sự kết hợp của ui-view autoscroll=true, $stateChangeStart, $locationChangeStart, $uiViewScrollProvider.useAnchorScroll(), $provide('$uiViewScroll', ...), và nhiều người khác, tôi không thể có được cuộn-to-top-on-mới-trang để làm việc như mong đợi.

Đây cuối cùng là những gì làm việc cho tôi. Nó nắm bắt PushState và thay thếState và chỉ cập nhật vị trí cuộn khi các trang mới được điều hướng đến (nút quay lại / chuyển tiếp giữ vị trí cuộn của chúng):

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

Điều này làm việc tuyệt vời cho trường hợp sử dụng của tôi. Tôi đang sử dụng Angular UI-Router với các khung nhìn lồng nhau (sâu vài cấp). Cảm ơn!
Joshua Powell

FYI - bạn cần ở chế độ HTML5 ở góc để sử dụng pushState: $locationProvider.html5Mode(true); @wkronkel có cách nào để thực hiện điều này khi không sử dụng chế độ HTML5 ở góc? Nguyên nhân tải lại trang đang gây ra lỗi 404 do chế độ html 5 đang hoạt động
britztopher

@britztopher Bạn có thể tham gia vào các sự kiện tích hợp và duy trì lịch sử của riêng mình, phải không?
Casey

2
bạn nối cái này .runvào đâu các appđối tượng góc cạnh của bạn ?
Philll_t

1
@Philll_t: Có, runchức năng là một phương thức có sẵn trên mọi đối tượng mô-đun góc.
Hinrich

18

Đây là giải pháp ngắn gọn (dường như) mạnh mẽ, đầy đủ và (khá) của tôi. Nó sử dụng kiểu tương thích thu nhỏ (và truy cập angular.module (NAME) vào mô-đun của bạn).

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

PS Tôi thấy rằng điều tự động ghi không có tác dụng cho dù được đặt thành đúng hay sai.


1
Hmmm .. điều này cuộn chế độ xem lên trên cùng trước và sau đó chế độ xem thay đổi trên màn hình đối với tôi.
Strelok 2/2/2015

Giải pháp sạch tôi đang tìm kiếm. Nếu bạn nhấp vào "trở lại", nó sẽ đưa bạn đến nơi bạn rời đi - điều này có thể không được mong muốn đối với một số người, nhưng tôi thích nó trong trường hợp của tôi.
mjoyce91

15

Sử dụng Bộ định tuyến UI angularjs, những gì tôi đang làm là:

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

Với:

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

Nó không bao giờ thất bại trên bất kỳ trang nào, và nó không phải là toàn cầu.


1
Ý tưởng tuyệt vời, bạn có thể làm cho nó ngắn hơn bằng cách sử dụng:onEnter: scrollContent
zucker

2
Những gì bạn đặt nơi bạn đã viết // Your favorite scroll method here?
Đặc vụ Zebra

4
Thời gian tốt: Tôi đã hai dòng trên nhận xét này trong mã gốc, thử ui-router-title . Tôi sử dụng $('html, body').animate({ scrollTop: -10000 }, 100);
Léon Pelletier

1
Haha, tuyệt quá Lạ tho, nó không hoạt động với tôi mọi lúc. Tôi có một số chế độ xem quá ngắn, chúng không có cuộn và hai chế độ xem có cuộn, khi tôi đặt onEnter: scrollContenttrên mỗi chế độ xem, nó chỉ hoạt động ở chế độ xem / tuyến đầu tiên, không phải ở chế độ xem thứ hai. Lạ thật.
Đặc vụ Zebra

1
Tôi không chắc chắn, không, nó chỉ không di chuyển lên đầu khi cần. Khi tôi nhấp vào giữa các 'tuyến đường', một số trong số chúng vẫn đang cuộn đến vị trí của chế độ xem ng, thay vì trên cùng tuyệt đối của trang kèm theo. Điều đó có ý nghĩa?
Đặc vụ Zebra

13

FYI cho bất kỳ ai gặp phải vấn đề được mô tả trong tiêu đề (như tôi đã làm), người cũng đang sử dụng plugin AngularUI Router ...

Khi được hỏi và trả lời trong câu hỏi SO này, bộ định tuyến góc cạnh nhảy xuống cuối trang khi bạn thay đổi tuyến đường.
Không thể hiểu tại sao tải trang ở dưới cùng? Vấn đề tự động định tuyến UI-Router

Tuy nhiên, như câu trả lời, bạn có thể tắt hành vi này bằng cách nói autoscroll="false"trên ui-view.

Ví dụ:

<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
Của tôi không nhảy xuống đáy, nó chỉ giữ nguyên vị trí cuộn thay vì lên đỉnh.
Stephen Smith

5
Điều này không trả lời câu hỏi. Tôi đã gặp vấn đề tương tự - khi tuyến đường thay đổi, mức cuộn chỉ ở cùng một chỗ thay vì cuộn lên trên cùng. Tôi có thể làm cho nó di chuyển lên trên cùng, nhưng chỉ bằng cách thay đổi hàm băm, điều mà tôi không muốn làm vì những lý do rõ ràng.
Sẽ

7

bạn có thể sử dụng javascript này

$anchorScroll()

3
Chà, nó không đơn giản trong angularjs. Giải pháp của bạn chỉ có giá trị trong jquery. Những gì tôi đang tìm kiếm là một cách thanh lịch để làm điều này trong angularjs
Matias Gonzalez

4
Bạn đã thử sử dụng $ anchorScroll () chưa, nó được ghi lại là docs.angularjs.org/api/ng.%24anchorScroll .
CodeIsLife

1
FYI Nếu bạn chỉ định một vị trí có $location.hash('top')trước $anchorScroll()các nút trình duyệt tiến / lùi mặc định không còn hoạt động; họ tiếp tục chuyển hướng bạn đến hàm băm trong URL
Roy

7

Nếu bạn sử dụng bộ định tuyến ui, bạn có thể sử dụng (đang chạy)

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

Tôi biết điều này sẽ hoạt động, tôi sử dụng rộng rãi "$ stateChangeSuccess", nhưng vì một số lý do $ window.scrollTo (0,0) không hoạt động với điều đó.
Abhas

4

Tôi tìm thấy giải pháp này. Nếu bạn đi đến một khung nhìn mới, chức năng sẽ được thực thi.

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

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

3

Đặt autoScroll thành true không phải là mẹo đối với tôi, vì vậy tôi đã chọn một giải pháp khác. Tôi đã xây dựng một dịch vụ móc nối mỗi khi tuyến thay đổi và sử dụng dịch vụ $ anchorScroll tích hợp để cuộn lên trên cùng. Làm việc cho tôi :-).

Dịch vụ:

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

Đăng ký:

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

3

Đến bữa tiệc muộn một chút, nhưng tôi đã thử mọi phương pháp có thể, và không có gì hoạt động chính xác. Đây là giải pháp tao nhã của tôi:

Tôi sử dụng bộ điều khiển chi phối tất cả các trang của mình bằng bộ định tuyến ui. Nó cho phép tôi chuyển hướng người dùng không được xác thực hoặc xác thực đến một vị trí thích hợp. Hầu hết mọi người đặt phần mềm trung gian của họ trong cấu hình ứng dụng của họ, nhưng tôi yêu cầu một số yêu cầu http, do đó, bộ điều khiển toàn cầu hoạt động tốt hơn đối với tôi.

Index.html của tôi trông giống như:

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

InitCtrl.js của tôi trông giống như:

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

Tôi đã thử mọi tùy chọn có thể, phương pháp này hoạt động tốt nhất.


1
Đây là người duy nhất làm việc với vật liệu góc đối với tôi, cũng đặt id="top-nav"yếu tố chính xác là rất quan trọng.
BatteryAcid

2

Tất cả các câu trả lời trên phá vỡ hành vi trình duyệt dự kiến. Điều mà hầu hết mọi người muốn là thứ gì đó sẽ cuộn lên trên cùng nếu đó là trang "mới", nhưng quay lại vị trí trước đó nếu bạn đến đó thông qua nút Quay lại (hoặc Chuyển tiếp).

Nếu bạn giả sử chế độ HTML5, điều này hóa ra rất dễ dàng (mặc dù tôi chắc rằng một số người thông minh ngoài kia có thể tìm ra cách làm cho điều này thanh lịch hơn!):

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

Cách thức hoạt động của nó là bộ định tuyến giả định rằng nó sẽ di chuyển lên trên cùng, nhưng trì hoãn một chút để cung cấp cho trình duyệt một cơ hội để kết thúc. Nếu trình duyệt sau đó thông báo cho chúng tôi rằng thay đổi là do điều hướng Quay lại / Chuyển tiếp, nó sẽ hủy thời gian chờ và cuộn không bao giờ xảy ra.

Tôi đã sử dụng documentcác lệnh thô để cuộn vì tôi muốn di chuyển đến toàn bộ trên cùng của cửa sổ. Nếu bạn chỉ muốn ui-viewcuộn, sau đó đặt autoscroll="my_var"nơi bạn điều khiển my_varbằng các kỹ thuật trên. Nhưng tôi nghĩ rằng hầu hết mọi người sẽ muốn cuộn toàn bộ trang nếu bạn đang đi đến trang là "mới".

Ở trên sử dụng ui-router, mặc dù bạn có thể sử dụng ng-route thay vì hoán đổi $routeChangeSuccesscho $stateChangeSuccess.


Làm thế nào để bạn thực hiện điều này? Nơi nào bạn sẽ đặt nó trong mã tuyến đường góc thường xuyên như sau đây? 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' }) });
Đặc vụ Zebra

hmmm ... không hoạt động :-( Nó chỉ giết chết ứng dụng. .state('web', { url: '/webdev', templateUrl: 'partial-web.html', controller: function($scope) { $scope.clients = ['client1', 'client2', 'client3']; // I put it here } }) Tôi chỉ đang học thứ này, có lẽ tôi đang thiếu thứ gì đó: D
Đặc vụ Zebra

@AgentZebra Tối thiểu, bộ điều khiển sẽ cần lấy $ timeout làm đầu vào (vì mã của tôi tham chiếu nó), nhưng có lẽ quan trọng hơn, bộ điều khiển tôi đang đề cập cần phải sống ở mức trên một trạng thái riêng lẻ (bạn có thể bao gồm các câu lệnh trong bộ điều khiển cấp cao nhất của bạn). Đường cong học tập ban đầu của AngularJS thực sự rất khó khăn; chúc may mắn!
Dev93

2

Không có câu trả lời nào cung cấp giải quyết vấn đề của tôi. Tôi đang sử dụng một hình ảnh động giữa các chế độ xem và cuộn sẽ luôn xảy ra sau hình ảnh động. Giải pháp tôi tìm thấy để việc cuộn lên trên cùng xảy ra trước khi hoạt hình là chỉ thị sau:

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

            })
        }
    };
}]);

Tôi đã chèn nó vào quan điểm của tôi như sau:

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

2

Giải pháp đơn giản, thêm scrollPositionRestorationvào mô-đun tuyến đường chính được kích hoạt.
Như thế này:

const routes: Routes = [

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

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


0

Cuối cùng tôi đã nhận được những gì tôi cần.

Tôi cần phải cuộn lên trên cùng, nhưng muốn một số chuyển tiếp không

Bạn có thể kiểm soát điều này ở cấp độ tuyến đường.
Tôi đang kết hợp giải pháp trên bởi @wkonkel và thêm một noScroll: truetham số đơn giản vào một số khai báo tuyến đường. Sau đó, tôi đang nắm bắt điều đó trong quá trình chuyển đổi.

Tất cả trong tất cả: Điều này nổi lên trên cùng của trang trên các chuyển tiếp mới, nó không nổi lên đầu Forward/ Backchuyển tiếp và nó cho phép bạn ghi đè hành vi này nếu cần thiết.

Mã: (giải pháp trước đó cộng với tùy chọn noScroll bổ sung)

  // 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');

Đặt nó trong app.runkhối của bạn và tiêm $state...myApp.run(function($state){...})

Sau đó, nếu bạn không muốn cuộn lên đầu trang, hãy tạo một tuyến đường như thế này:

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

0

Điều này làm việc cho tôi bao gồm cả autoscroll

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.