Изменение маршрута не прокручивается вверх на новой странице

271

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

Я также нашел это в одном из своих приложений, и мне было интересно, как я могу получить это, чтобы прокрутить вверх. Я могу сделать это вручную, но я думаю, что должен быть другой элегантный способ сделать это, которого я не знаю.

Итак, есть ли элегантный способ прокрутки вверх при изменении маршрута?

Матиас Гонсалес
источник

Ответы:

579

Проблема в том, что ваш 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
это прокрутит ваш взгляд на этот div. Поэтому он будет прокручиваться вверх страницы, только если он окажется вверху страницы. В противном случае вам придется запустить $window.scrollTo(0,0)прослушиватель событий $ stateChangeSuccess
Филип Собчак
46

Просто поместите этот код для запуска

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

    window.scrollTo(0, 0);

});
xmaster
источник
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 выполняется после создания инжектора и используется для запуска приложения. Это означает, что при перенаправлении на новый вид маршрута слушатель в блоке 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');
})
wkonkel
источник
Это прекрасно работает для моего варианта использования. Я использую Angular UI-Router с вложенными представлениями (несколько уровней глубиной). Спасибо!
Джошуа Пауэлл
К вашему сведению - вам нужно быть в режиме HTML5 в угловом режиме, чтобы использовать pushState: $locationProvider.html5Mode(true); @wkronkel, есть ли способ сделать это, если вы не используете режим HTML5 в угловом режиме? Причина перезагрузки страницы выдает 404 ошибки из-за активного режима HTML 5
britztopher
@britztopher Вы можете подключиться к встроенным событиям и вести собственную историю, верно?
Кейси
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 Я обнаружил, что функция автопрокрутки не имеет никакого эффекта, независимо от того, установлено ли значение true или false.

Джеймс
источник
1
Хммм .. это прокручивает вид сначала сверху, а затем вид меняется на экране для меня.
Стрелок
Чистое решение, которое я искал. Если вы нажмете «назад», вы попадете туда, где остановились - это может быть нежелательно для некоторых, но мне нравится в моем случае.
mjoyce91
15

Используя Angularjs UI Router, я делаю так:

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

С участием:

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

Это никогда не терпит неудачу на любой странице, и это не глобально.

Леон Пеллетье
источник
1
Отличная идея, вы можете сделать это короче, используя:onEnter: scrollContent
zucker
2
Что ты положил туда, где написал // Your favorite scroll method here?
Агент Зебра
4
Хорошее время: я был на две строки выше этого комментария в исходном коде, пытаясь использовать ui-router-title . Я использую $('html, body').animate({ scrollTop: -10000 }, 100);
Леон Пеллетье
1
Хаха, отлично! Странно, но это не работает для меня все время. У меня есть некоторые виды, которые настолько короткие, что у них нет свитка, и два вида, у которых есть свиток, когда я размещаю их onEnter: scrollContentна каждом виде, он работает только на первом виде / маршруте, а не на втором. Странный.
Агент Зебра
1
Я не уверен, нет, он просто не прокручивается вверх, когда должен. Когда я нажимаю между «маршрутами», некоторые из них по-прежнему прокручиваются туда, где находится ng-представление, а не в абсолютной верхней части вложенной страницы. Имеет ли это смысл?
Агент Зебра
13

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

Как было задано и получено в ответе на этот вопрос, маршрутизатор angular-ui переходит к нижней части страницы при изменении маршрутов.
Не можете понять, почему страница загружается внизу? Автопрокрутка углового 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

mg1075
источник
16
Шахта не прыгает на дно, она просто сохраняет положение прокрутки, вместо того, чтобы идти наверх.
Стивен Смит
5
Это не отвечает на вопрос. У меня та же проблема - когда маршрут меняется, уровень прокрутки просто остается на том же месте, а не прокручивается до самого верха. Я могу заставить его пролистать до вершины, но только изменив хеш, что я не хочу делать по понятным причинам.
Будет
7

Вы можете использовать этот JavaScript

$anchorScroll()
CodeIsLife
источник
3
Ну, это не так просто в angularjs. Ваше решение действительно только в jquery. То, что я ищу, это элегантный способ сделать это в angularjs
Матиас Гонсалес
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);
});
Colzak
источник
Я знаю, что это должно работать, я широко использую «$ 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

Установка автопрокрутки в 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();
}]);
asp_net
источник
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');
       });
    });
});

Я перепробовал все возможные варианты, этот метод работает лучше всего.

BuffMcBigHuge
источник
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-маршрутизатор, хотя вы можете использовать нг-маршрут вместо путем замены $routeChangeSuccessна $stateChangeSuccess.

Dev93
источник
Как вы реализуете это? Где бы вы поместили его в обычный код 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
Агент Зебра
@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>
aBertrand
источник
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параметр в некоторые объявления маршрутов. Тогда я ловлю это в переходе.

В целом: This поплавков в верхней части страницы на новых переходах, не всплывают на вершину на 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" >

Тея
источник