Angular 2 Scroll to top on Route Change: почему это важно и как реализовать
Представьте, что вы листаете длинную страницу с документацией, доходите до середины, кликаете на ссылку — и оказываетесь внизу нового раздела. Знакомо? Именно эту проблему решает механика Angular 2 scroll to top on route change. Без неё пользовательский опыт страдает: приходится вручную крутить колёсико мыши или жать Home, что раздражает и увеличивает отказы.
В этой статье мы разберём, как заставить Angular автоматически скроллить страницу к верху при каждом переходе между маршрутами. Вы узнаете не только готовое решение, но и поймёте, почему это работает именно так. Статья написана для новичков, но будет полезна и опытным разработчикам, которые хотят освежить знания.
Почему Angular не делает это "из коробки"?
Angular — мощный SPA-фреймворк, но его поведение при навигации может удивлять. По умолчанию при смене маршрута браузер не сбрасывает позицию скролла. Почему? Потому что Angular Router рассматривает переход как замену компонента в <router-outlet>, а не как загрузку новой страницы. Браузер же думает: «Я всё ещё на том же документе, зачем мне скроллиться?».
Это особенно заметно, если у вас есть длинные списки, таблицы или документация. Пользователь кликает по ссылке внизу страницы — и новый контент открывается, но скролл остаётся внизу. Решение этой проблемы — явно указать Angular: «Эй, при каждом переходе крути наверх!».
Два подхода к решению
Существует два основных способа реализовать scroll to top on route change в Angular 2+:
- Глобальный обработчик в AppComponent — простой, но работает не всегда корректно с вложенными маршрутами.
- Использование Route Config — более гибкий, позволяет настраивать поведение для каждого маршрута отдельно.
Мы рассмотрим оба, но рекомендую остановиться на втором — он даёт больше контроля.
Способ 1: Простой глобальный обработчик (для быстрого старта)
Откройте ваш app.component.ts (или корневой компонент) и добавьте подписку на события Router. Код на TypeScript:
import { Component, OnInit } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs/operators';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
constructor(private router: Router) {}
ngOnInit() {
this.router.events.pipe(
filter(event => event instanceof NavigationEnd)
).subscribe(() => {
window.scrollTo(0, 0);
});
}
}
Здесь мы подписываемся на события роутера, фильтруем только NavigationEnd (момент, когда переход завершён), и вызываем window.scrollTo(0, 0). Просто, но есть нюанс: если у вас есть вложенные маршруты (например, дочерние роуты внутри компонента), этот код может срабатывать слишком рано или не срабатывать вовсе.
Способ 2: Правильное решение с использованием Route Config (рекомендуется)
Этот подход использует встроенную опцию scrollPositionRestoration в Angular Router. Начиная с Angular 6.1, появилась возможность настроить автоматическое восстановление/сброс скролла прямо в конфигурации маршрутизации.
Откройте ваш app-routing.module.ts и добавьте опцию scrollPositionRestoration: 'top' в RouterModule.forRoot():
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: 'contact', component: ContactComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes, {
scrollPositionRestoration: 'top' // <-- вот это
})],
exports: [RouterModule]
})
export class AppRoutingModule { }
Одна строчка — и все переходы будут автоматически скроллить к верху. Но будьте внимательны: если у вас есть маршруты, где скролл нужно сохранять (например, список с пагинацией), используйте scrollPositionRestoration: 'enabled' — он будет восстанавливать позицию при возврате назад.
Способ 3: Тонкая настройка с помощью Anchor Scrolling
Иногда нужно не просто скроллить наверх, а переходить к конкретному элементу на странице (например, #section1). Для этого используйте фрагменты URL (anchor links):
imports: [RouterModule.forRoot(routes, {
anchorScrolling: 'enabled',
scrollPositionRestoration: 'enabled'
})]
Теперь в шаблоне можно писать:
<a [routerLink]="['/page']" fragment="section1">Перейти к разделу 1</a>
Angular сам найдёт элемент с id="section1" и прокрутит к нему. Комбинация scrollPositionRestoration и anchorScrolling даёт полный контроль.
Что делать, если решение не работает?
Бывает, что код написан, а скролл всё равно не срабатывает. Проверьте три вещи:
- Версия Angular:
scrollPositionRestorationдоступен с Angular 6.1. Если у вас старше — обновите или используйте первый способ. - Порядок импортов: убедитесь, что вы импортируете
RouterModule.forRoot()именно в корневом модуле, а не в дочерних. - Конфликт с пользовательскими скроллами: если вы используете библиотеки вроде
ngx-scrollbarили кастомный скролл-контейнер, стандартныйwindow.scrollToможет не работать. В таком случае нужно скроллить сам контейнер.
Пример для кастомного контейнера (если у вас overflow: auto на <div class="content">):
this.router.events.pipe(
filter(event => event instanceof NavigationEnd)
).subscribe(() => {
const content = document.querySelector('.content');
if (content) {
content.scrollTop = 0;
}
});
Практический пример: полный код для реального проекта
Давайте соберём всё вместе. Допустим, у вас есть типичное приложение с боковым меню и контентом. Чтобы Angular 2 scroll to top on route change работал идеально, сделайте так:
Шаг 1. В app-routing.module.ts:
const routes: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: '