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

267

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

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

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

Теги:
scroll
angularjs-routing

18 ответов

546

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

Решение состоит в добавлении autoscroll="true" к элементу ngView:

<div class="ng-view" autoscroll="true"></div>
  • 1
    Для меня это работа, прокрутка страницы в порядке, но я использую scrollTo с директивой smoothScroll, есть ли способ сделать небольшую прокрутку в начало? Кроме того, ваше решение прокрутить вверх, а не на страницу, чтобы прокрутить до верх страницы?
  • 19
    Состояние документов Если атрибут установлен без значения, включите прокрутку . Таким образом, вы можете сократить код до <div class="ng-view" autoscroll></div> и он будет работать точно так же (если вы не хотите сделать прокрутку условной).
Показать ещё 10 комментариев
44

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

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

    window.scrollTo(0, 0);

});
  • 5
    $window.scrollTop(0,0) работает для меня лучше, чем автопрокрутка. В моем случае у меня есть представления, которые входят и выходят с переходами.
  • 1
    Это также работает лучше для меня, чем autoscroll = "true". По какой-то причине автопрокрутка была слишком поздней, она прокручивалась до самого верха после того, как новый вид уже был виден.
Показать ещё 6 комментариев
34

Этот код отлично подойдет для меня.. Надеюсь, он также отлично подойдет вам. Все, что вам нужно сделать, это просто вставить $anchorScroll в ваш блок выполнения и применить функцию прослушивателя к корневому объекту, как я сделал в приведенном ниже примере.

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

Здесь порядковый номер модуля Angularjs:

  • app.config()
  • app.run()
  • directive compile functions (if they are found in the dom)
  • app.controller()
  • directive link functions (again, if found)

RUN BLOCK выполняются после создания инжектора и используются для запуска приложения. Это означает, что при перенаправлении на новое представление маршрута слушатель в блоке запуска вызывает

$anchorScroll()

и теперь вы можете увидеть, что свиток начинается сверху с новым маршрутизированным представлением:)

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

Вот мое (казалось бы) надежное, полное и (справедливо) сжатое решение. Он использует стиль, совместимый с миниатюрами (и доступ к вашему модулю angular.module(NAME)).

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

PS Я обнаружил, что объект autoscroll не имел никакого эффекта, установлен ли true или false.

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

Используя угловой UI Router, я делаю следующее:

    .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 ?
Показать ещё 7 комментариев
13

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

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

Если вы используете ui-router, вы можете использовать (при запуске)

$rootScope.$on("$stateChangeSuccess", function (event, currentState, previousState) {
    $window.scrollTo(0, 0);
});
  • 0
    Я знаю, что это должно работать, я широко использую «$ stateChangeSuccess», но по какой-то причине $ window.scrollTo (0,0) не работает с этим.
7

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

$anchorScroll()
  • 3
    Ну, это не так просто в angularjs. Ваше решение действительно только в jquery. То, что я ищу, это элегантный способ сделать это в angularjs
  • 4
    Вы пытались использовать $ anchorScroll (), это задокументировано docs.angularjs.org/api/ng.%24anchorScroll .
Показать ещё 1 комментарий
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();
}]);
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

Немного поздно вечеринке, но я пробовал все возможные методы, и ничего не работало правильно. Вот мое изящное решение:

Я использую контроллер, который управляет всеми моими страницами с помощью 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
    Это единственный, который работал с angular-material для меня, также важно было поместить id="top-nav" на правильный элемент.
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;
}

Как это работает, маршрутизатор предполагает, что он будет прокручиваться вверх, но немного задерживается, чтобы дать браузеру возможность закончить. Если браузер уведомляет нас о том, что изменение произошло из-за навигации "Назад/Вперед", оно отменяет тайм-аут, и прокрутка никогда не возникает.

Я использовал команды raw document для прокрутки, потому что я хочу перейти ко всей вершине окна. Если вы просто хотите, чтобы ваш ui-view прокручивался, установите autoscroll="my_var", где вы управляете my_var, используя вышеприведенные методы. Но я думаю, что большинство людей захотят прокрутить всю страницу, если вы переходите на страницу как "новое".

В приведенном выше примере используется ui-router, но вместо этого вы можете использовать ng-route, заменив $routeChangeSuccess на $stateChangeSuccess.

  • 0
    Как вы реализуете это? Где бы вы поместили его в обычный код маршрута 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' }) });
  • 0
    @AgentZebra поместите код в свой контроллер.
Показать ещё 2 комментария
0

Простое решение, добавьте scrollPositionRestoration в основной модуль маршрута включен.
Как это:

const routes: Routes = [

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

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

Это сработало для меня, включая автопрокрутку

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

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

Попробуйте http://ionicframework.com/docs/api/service/ $ionicScrollDelegate/

Он прокручивается до верхней части списка scrollTop()

Ещё вопросы

Сообщество Overcoder
Наверх
Меню