Зміна маршруту не прокручується до нової сторінки


271

Я знайшов якусь небажану, принаймні для мене, поведінку, коли маршрут змінюється. На кроці 11 підручника http://angular.github.io/angular-phonecat/step-11/app/#/phones можна переглянути список телефонів. Якщо прокрутити донизу і натиснути один із останніх, ви побачите, що прокрутка знаходиться не вгорі, натомість знаходиться посередині.

Я знайшов це і в одному зі своїх додатків, і мені було цікаво, як я можу зробити це, щоб прокрутити до вершини. Я можу це робити щоденно, але я думаю, що повинен бути інший елегантний спосіб зробити це, якого я не знаю.

Отже, чи є елегантний спосіб прокручування до вершини, коли маршрут змінюється?

Відповіді:


578

Проблема полягає в тому, що ваш ngView зберігає позицію прокрутки, коли він завантажує новий вид. Ви можете доручити $anchorScroll"прокручувати область перегляду після оновлення подання" ( документи трохи розпливчасті, але прокрутка тут означає прокручування до початку нового виду).

Рішення полягає в тому, щоб додати autoscroll="true"до елемента ngView:

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

1
Ця робота для мене: сторінка прокручується чудово до вершини, але я використовую scrollTo з директивою smoothScroll, чи є спосіб зробити плавний прокручування до верху? вгорі сторінки?
xzegga

19
Стан Документів Якщо атрибут встановлений без значення, увімкніть прокрутку . Таким чином, ви можете скоротити код до справедливого, <div class="ng-view" autoscroll></div>і він працює точно так само (якщо ви не хотіли умовно прокручувати).
Даніель Люцці

7
це не працює для мене, документ не каже, що прокрутить до вершини
Pencilcheck

6
Я вважаю, що якщо для автоматичного прокрутки встановлено значення true, тоді, коли користувач 'повертається назад', вони повертаються вгору сторінки, а не там, де вони її залишили, що є небажаною поведінкою.
axzr

4
це прокрутить ваш погляд до цього діва. Таким чином, він буде прокручуватися до верхньої частини сторінки, лише якщо він трапиться вгорі сторінки. Інакше вам доведеться запустити $window.scrollTo(0,0)слухача подій у $ stateChangeSuccess
Філіп Собчак

46

Просто поставте цей код для запуску

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

    window.scrollTo(0, 0);

});

6
$window.scrollTop(0,0)працює краще для мене, ніж автоскролл. У моєму випадку у мене є перегляди, які входять і залишаються з переходами.
IsidroGH

1
Це також працює для мене краще, ніж autoscroll = "true". Автоскрол чомусь запізнився, він прокрутить до вершини після того, як новий вид уже буде видно.
Григорій Болкенстійн

5
Це найкраще працювало для мене: $ rootScope. $ On ('$ stateChangeSuccess', function () {$ ("html, body"). Animate ({scrollTop: 0}, 200);});
Джеймс Гентес

$ window.scrollTop має бути $ window.scrollTo, інакше він говорить, що його не існує. Це рішення чудово працює!
Програмне забезпечення пророків

@JamesGentes мені теж потрібен був цей. Дякую.
Маккензі Браун

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 виконується після створення інжектора і використовується для швидкого запуску програми. Це означає, що коли ви перенаправляєтесь на новий вид маршруту, слухач у блоці запуску викликає

$ anchorScroll ()

і тепер ви можете побачити прокрутку починає до вершини з новим маршрутизованим видом :)


Нарешті, рішення, яке працює з клейкими заголовками! Дякую!
Фабіо

31

Через годину-дві спроби кожної комбінації ui-view autoscroll=true, $stateChangeStart, $locationChangeStart, $uiViewScrollProvider.useAnchorScroll(),$provide('$uiViewScroll', ...) , і багато інших, я не міг отримати сувій-вгору-на-нової-сторінці для роботи , як і очікувалося.

Це в кінцевому підсумку працювало для мене. Він фіксує pushState і substituState і оновлює позицію прокрутки лише тоді, коли переходять нові сторінки (кнопка назад / вперед зберігає свої позиції прокрутки):

.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 з вкладеними видами (на кілька рівнів глибиною). Дякую!
Джошуа Пауелл

FYI - вам потрібно бути в режимі HTML5 в кутовому, щоб використовувати pushState: $locationProvider.html5Mode(true); @wkronkel Чи є спосіб досягти цього, коли не використовується режим HTML5 в кутовому? Тому що перезавантаження сторінки кидає 404 помилки через активність режиму html 5
britztopher

@britztopher Ви можете підключитися до вбудованих подій та підтримувати власну історію, правда?
Кейсі

2
куди ви додаєте це .run? кутовий ваш appоб’єкт?
Philll_t

1
@Philll_t: Так, runфункція - це метод, доступний для кожного об'єкта кутового модуля.
Гінріх

18

Ось моє (здавалося б) надійне, повне і (досить) стисле рішення. Він використовує стиль, сумісний із мінімізацією (та доступ до вашого модуля).

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

PS Я виявив, що функція автоматичного прокрутки не впливає на встановлення значення true та false.


1
Гммм .. це спочатку прокручує погляд до верху, а потім погляд на екрані для мене змінюється.
Стрелок

Чисте рішення я шукав. Якщо ви натиснете "назад", це переведе вас туди, де ви зупинилися - для деяких це може не бути бажаним, але мені це подобається в моєму випадку.
mjoyce91

15

Використовуючи маршрутизатор інтерфейсу angularjs, я роблю це:

    .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?
Агент Зебра

4
Хороший час: я був на два рядки вище цього коментаря в оригінальному коді, намагаючись заголовок ui-router . Я використовую $('html, body').animate({ scrollTop: -10000 }, 100);
Леон Пелтьє,

1
Ха-ха, чудово! Дивно тхо, це не працює для мене весь час. У мене такі короткі погляди, що вони не мають прокрутки, і два представлення, які мають прокрутку, коли я розміщую onEnter: scrollContentна кожному перегляді, він працює лише на першому огляді / маршруті, а не на другому. Дивно.
Агент Зебра

1
Я не впевнений, ні, він просто не прокручується вгору, коли слід. Коли я клацаю між "маршрутами", деякі з них все ще прокручуються туди, де знаходиться ng-перегляд, а не абсолютна вершина вкладеної сторінки. Чи має це сенс?
Агент Зебра

13

FYI для тих, хто стикається з проблемою, описаною в заголовку (як я це робив), хто також використовує маршрутизатор AngularUI плагін ...

На запитання і відповідь у цьому запитанні ТА, кутовий маршрутизатор підстрибує внизу сторінки, коли ви змінюєте маршрути.
Ви не можете зрозуміти, чому сторінка завантажується знизу? Випуск автоматичного прокручування UI-маршрутизатора

Однак, як йдеться у відповіді, ви можете вимкнути цю поведінку, сказавши 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
Це не відповідає на запитання. У мене така ж проблема - коли маршрут змінює рівень прокрутки, він просто залишається там же, а не прокручується до верху. Я можу змусити його прокручуватися до вершини, але лише змінивши хеш, що я не хочу робити з зрозумілих причин.
Буде чи

7

ви можете використовувати цей javascript

$anchorScroll()

3
Ну, це не так просто в angularjs. Ваше рішення дійсне лише в jquery. Я шукаю елегантний спосіб зробити це у angularjs
Matias Gonzalez

4
Чи намагалися ви використовувати $ anchorScroll (), це документально підтверджено docs.angularjs.org/api/ng.%24anchorScroll .
CodeIsLife

1
FYI Якщо ви вказали місце розташування, $location.hash('top')перш ніж $anchorScroll()типові кнопки браузера вперед / назад більше не працюють; вони продовжують переадресовувати вас на хеш у URL-адресі
Рой

7

Якщо ви користуєтесь ui-роутером, ви можете використовувати (на ходу)

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

Я знаю, що це має працювати, я широко використовую "$ stateChangeSuccess", але чомусь $ window.scrollTo (0,0) не працює з цим.
Абхас

4

Я знайшов це рішення. Якщо перейти до нового перегляду, функція буде виконуватися.

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

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

3

Встановлення функції autoScroll у справжньому для мене не було хитрістю, тому я вибрав інше рішення. Я створив службу, яка підключається щоразу, коли маршрут змінюється і використовує вбудований сервіс $ 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-роутера. Це дозволяє мені переадресовувати користувачів, які не мають автентифікацію або перевірку, на відповідне місце. Більшість людей поміщають проміжне програмне забезпечення у конфігурацію програми, але мені потрібні деякі запити 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-роутер, хоча ви можете замість цього використовувати ng-route, заміняючи $routeChangeSuccessна $stateChangeSuccess.


Як ви це реалізуєте? Де ви б помістили його у звичайному коді углових маршрутів, таких як наступний? 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
Агент Зебра

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.