Angular 2 Прокрутите вверх при изменении маршрута

296

В моем приложении Angular 2, когда я прокручиваю страницу вниз и щелкаю ссылку внизу страницы, она меняет маршрут и переходит на следующую страницу, но не переходит в верхнюю часть страницы. В результате, если первая страница длинная, а на 2-й странице мало содержимого, создается впечатление, что на 2-й странице не хватает содержимого. Так как содержимое видимо только при прокрутке пользователя вверх страницы.

Я могу прокрутить окно до верхней части страницы в ngInit компонента, но есть ли лучшее решение, которое может автоматически обрабатывать все маршруты в моем приложении?

Навид Ахмед
источник
20
Начиная с Angular 6.1 мы можем использовать {scrollPositionRestoration: 'enabled'} на загруженных модулях или только в app.module, и он будет применяться ко всем маршрутам. RouterModule.forRoot(appRoutes, { scrollPositionRestoration: 'enabled' })
Манвал
Muito obrigado sua solução funcionou perfeitamente para mim :)
Рафаэль Годой
не один человек упомянул фокус? это более важно, чем когда-либо прежде, чтобы должным образом поддерживать специальные возможности / средства чтения с экрана, и если вы просто прокрутите вверх, не обращая внимания на фокус, то нажатие следующей вкладки может перейти к нижней части экрана.
Simon_Weaver

Ответы:

386

Вы можете зарегистрировать прослушиватель изменения маршрута в своем главном компоненте и прокручивать вверх для изменения маршрута.

import { Component, OnInit } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';

@Component({
    selector: 'my-app',
    template: '<ng-content></ng-content>',
})
export class MyAppComponent implements OnInit {
    constructor(private router: Router) { }

    ngOnInit() {
        this.router.events.subscribe((evt) => {
            if (!(evt instanceof NavigationEnd)) {
                return;
            }
            window.scrollTo(0, 0)
        });
    }
}
Гильерме Мейрелеш
источник
11
window.scrollTo(0, 0)является более кратким document.body.scrollTop = 0;, и более читаемым ИМО.
Марк Э. Хааз
10
Кто-нибудь заметил, что даже после реализации, проблема сохраняется в браузере Safari Iphone. Есть предположения?
rgk
1
@ mehaase Похоже, ваш ответ самый лучший. У меня не работает window.body.scrollTop на рабочем столе Firefox. Так что спасибо тебе !
KCarnaille
3
Это сработало для меня, но нарушает поведение кнопки «назад» по умолчанию. Возвращаясь следует помнить предыдущую позицию прокрутки.
JackKalish
6
Это сработало !! Хотя я добавил, $("body").animate({ scrollTop: 0 }, 1000);а не window.scrollTo(0, 0)анимировать плавную прокрутку вверх
Manubhargav
360

Угловой 6.1 и позже :

В Angular 6.1 (выпущено в 2018-07-25) добавлена ​​встроенная поддержка для решения этой проблемы с помощью функции «Восстановление положения прокрутки маршрутизатора». Как описано в официальном блоге Angular , вам просто нужно включить это в конфигурации маршрутизатора следующим образом:

RouterModule.forRoot(routes, {scrollPositionRestoration: 'enabled'})

Кроме того, блог гласит: «Ожидается, что это станет по умолчанию в будущем выпуске». Пока что этого не произошло (по состоянию на Angular 8.2), но в конечном итоге вам вообще не нужно ничего делать в вашем коде, и это будет работать корректно из коробки.

Вы можете увидеть более подробную информацию об этой функции и о том, как настроить это поведение в официальных документах .

Угловой 6.0 и ранее :

Хотя отличный ответ @ GuilhermeMeireles исправляет исходную проблему, он вводит новую, нарушая нормальное поведение, которое вы ожидаете при переходе назад или вперед (с помощью кнопок браузера или с помощью Location в коде). Ожидаемое поведение заключается в том, что при переходе обратно на страницу она должна оставаться прокручиваемой вниз до того же места, в котором она была при нажатии на ссылку, но прокрутка к вершине при переходе на каждую страницу, очевидно, разрушает это ожидание.

Приведенный ниже код расширяет логику для обнаружения такого рода навигации, подписавшись на последовательность PopStateEvent в Location и пропустив логику прокрутки вверх, если вновь появившаяся страница является результатом такого события.

Если страница, с которой вы переходите назад, достаточно длинна, чтобы покрыть весь видовой экран, положение прокрутки восстанавливается автоматически, но, как правильно указал @JordanNelson, если страница короче, вам нужно отслеживать исходную позицию прокрутки y и восстанавливать ее. явно, когда вы вернетесь на страницу. Обновленная версия кода также охватывает этот случай, всегда явно восстанавливая положение прокрутки.

import { Component, OnInit } from '@angular/core';
import { Router, NavigationStart, NavigationEnd } from '@angular/router';
import { Location, PopStateEvent } from "@angular/common";

@Component({
    selector: 'my-app',
    template: '<ng-content></ng-content>',
})
export class MyAppComponent implements OnInit {

    private lastPoppedUrl: string;
    private yScrollStack: number[] = [];

    constructor(private router: Router, private location: Location) { }

    ngOnInit() {
        this.location.subscribe((ev:PopStateEvent) => {
            this.lastPoppedUrl = ev.url;
        });
        this.router.events.subscribe((ev:any) => {
            if (ev instanceof NavigationStart) {
                if (ev.url != this.lastPoppedUrl)
                    this.yScrollStack.push(window.scrollY);
            } else if (ev instanceof NavigationEnd) {
                if (ev.url == this.lastPoppedUrl) {
                    this.lastPoppedUrl = undefined;
                    window.scrollTo(0, this.yScrollStack.pop());
                } else
                    window.scrollTo(0, 0);
            }
        });
    }
}
Фернандо Эчеверрия
источник
2
Это должно происходить либо в компоненте приложения напрямую, либо в отдельном компоненте, используемом в нем (и, следовательно, совместно используемом всем приложением). Например, я включил его в верхний компонент панели навигации. Вы не должны быть включены во все ваши компоненты.
Фернандо Эчеверриа
3
Вы можете сделать это, и это сделает код более совместимым с другими не браузерными платформами. См. Stackoverflow.com/q/34177221/2858481 для деталей реализации.
Фернандо Эчеверриа
3
Если вы нажмете и удержите кнопку «назад / вперед» в современных браузерах, появится меню, которое позволит вам перейти к местам, отличным от вашего непосредственно предыдущего / следующего. Это решение нарушается, если вы делаете это. Это крайний случай для большинства, но стоит упомянуть.
adamdport
1
Есть ли способ включить «Восстановление положения прокрутки маршрутизатора» для вложенных элементов или он работает только для body?
1858
61

Начиная с Angular 6.1, теперь вы можете избежать хлопот и перейти extraOptionsк своему RouterModule.forRoot()второму параметру, а также можете указать scrollPositionRestoration: enabledAngular прокручивать вверх при каждом изменении маршрута.

По умолчанию вы найдете это в app-routing.module.ts:

const routes: Routes = [
  {
    path: '...'
    component: ...
  },
  ...
];

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

Angular Официальные Документы

Абдул Рафай
источник
3
Несмотря на то, что ответ выше является более описательным, мне нравится, что этот ответ сказал мне точно, куда это нужно идти
ryanovas
32

Вы можете написать это более кратко, используя преимущества наблюдаемого filterметода:

this.router.events.filter(event => event instanceof NavigationEnd).subscribe(() => {
      this.window.scrollTo(0, 0);
});

Если у вас возникли проблемы с прокруткой вверх при использовании sidenav Angular Material 2, это поможет. У окна или тела документа не будет полосы прокрутки, поэтому вам нужно получить sidenavконтейнер содержимого и прокрутить этот элемент, в противном случае попробуйте прокрутить окно по умолчанию.

this.router.events.filter(event => event instanceof NavigationEnd)
  .subscribe(() => {
      const contentContainer = document.querySelector('.mat-sidenav-content') || this.window;
      contentContainer.scrollTo(0, 0);
});

Кроме того, Angular CDK v6.x теперь имеет пакет прокрутки, который может помочь с обработкой прокрутки.

mtpultz
источник
2
Большой! Для меня это сработало -document.querySelector('.mat-sidenav-content .content-div').scrollTop = 0;
Амир Туги
Хорошие ребята ... на mtpultz & @AmirTugi. Разбираясь с этим прямо сейчас, и ты прибил это для меня, ура! Вероятно, это неизбежно приведет к тому, что я переверну свою боковую навигацию, поскольку материал 2 не очень хорош, когда панель инструментов md находится в позиции: исправлено (вверху) Если у вас, ребята, нет идей .... ????
Тим Харкер
Возможно, нашёл мой ответ ... stackoverflow.com/a/40396105/3389046
Тим Харкер
16

Если у вас есть рендеринг на стороне сервера, вы должны быть осторожны, чтобы не запускать код, используемый windowsна сервере, где эта переменная не существует. Это приведет к взлому кода.

export class AppComponent implements OnInit {
  routerSubscription: Subscription;

  constructor(private router: Router,
              @Inject(PLATFORM_ID) private platformId: any) {}

  ngOnInit() {
    if (isPlatformBrowser(this.platformId)) {
      this.routerSubscription = this.router.events
        .filter(event => event instanceof NavigationEnd)
        .subscribe(event => {
          window.scrollTo(0, 0);
        });
    }
  }

  ngOnDestroy() {
    this.routerSubscription.unsubscribe();
  }
}

isPlatformBrowserявляется функцией, используемой для проверки, является ли текущая платформа, на которой отображается приложение, браузером или нет. Мы даем это введено platformId.

Это также можно проверить на наличие переменной windows, чтобы быть в безопасности, как это:

if (typeof window != 'undefined')
Raptor
источник
1
Вам не нужно вводить PLATFORM_IDв constructorи давать это значение в качестве параметра в isPlatformBrowserметоде de ?
Пол Круйт
1
@PierreDuc Да, ответ неверный. isPlatformBrowserэто функция и всегда будет правдой. Я редактировал это сейчас.
Лазарь Любенович
Спасибо! Теперь это правильно! Только что проверил API: github.com/angular/angular/blob/…
Raptor
13

просто сделай это легко с помощью щелчка

в вашем основном компоненте HTML сделать ссылку #scrollContainer

<div class="main-container" #scrollContainer>
    <router-outlet (activate)="onActivate($event, scrollContainer)"></router-outlet>
</div>

в основной компонент .ts

onActivate(e, scrollContainer) {
    scrollContainer.scrollTop = 0;
}
СУББОТА
источник
Элемент, который нужно прокрутить, может быть не в scrollContainerпервом узле, возможно, вам придется немного покопаться в объекте, для меня он действительно работалscrollContainer .scrollable._elementRef.nativeElement.scrollTop = 0
Байрон Лопес,
13

Angular в последнее время представил новую функцию, внутри модуля угловой маршрутизации внести изменения, как показано ниже

@NgModule({
  imports: [RouterModule.forRoot(routes,{
    scrollPositionRestoration: 'top'
  })],
Пран Р.В.
источник
12

Лучший ответ - в обсуждении Angular GitHub ( изменение маршрута не прокручивается вверх на новой странице ).

Может быть, вы хотите идти наверх только в изменениях корневого маршрутизатора (не в дочерних, потому что вы можете загружать маршруты с ленивой загрузкой в ​​fe tabset)

app.component.html

<router-outlet (deactivate)="onDeactivate()"></router-outlet>

app.component.ts

onDeactivate() {
  document.body.scrollTop = 0;
  // Alternatively, you can scroll to top by using this other call:
  // window.scrollTo(0, 0)
}

Полные кредиты JoniJnm ( оригинальный пост )

zurfyx
источник
8

Вы можете добавить хук жизненного цикла AfterViewInit к своему компоненту.

ngAfterViewInit() {
   window.scrollTo(0, 0);
}
stillatmylinux
источник
7

Начиная с версии Angular 6.1, маршрутизатор предоставляет опцию конфигурации, которая называется scrollPositionRestorationдля этого сценария.

imports: [
  RouterModule.forRoot(routes, {
    scrollPositionRestoration: 'enabled'
  }),
  ...
]
Марти А
источник
4

Если вам нужно просто прокрутить страницу вверх, вы можете сделать это (не лучшее решение, но быстрое)

document.getElementById('elementId').scrollTop = 0;
Aliaksei
источник
4

Вот решение, которое я придумала. Я связал LocationStrategy с событиями Router. Использование LocationStrategy для установки логического значения, чтобы знать, когда пользователь в данный момент просматривает историю браузера. Таким образом, мне не нужно хранить кучу данных URL и y-scroll (что в любом случае не работает, так как все данные заменяются на основе URL). Это также решает крайний случай, когда пользователь решает удерживать кнопку «назад» или «вперед» в браузере и переходит назад или вперед на несколько страниц, а не на одну.

PS Я тестировал только на последних версиях IE, Chrome, FireFox, Safari и Opera (по состоянию на этот пост).

Надеюсь это поможет.

export class AppComponent implements OnInit {
  isPopState = false;

  constructor(private router: Router, private locStrat: LocationStrategy) { }

  ngOnInit(): void {
    this.locStrat.onPopState(() => {
      this.isPopState = true;
    });

    this.router.events.subscribe(event => {
      // Scroll to top if accessing a page, not via browser history stack
      if (event instanceof NavigationEnd && !this.isPopState) {
        window.scrollTo(0, 0);
        this.isPopState = false;
      }

      // Ensures that isPopState is reset
      if (event instanceof NavigationEnd) {
        this.isPopState = false;
      }
    });
  }
}
Sal_Vader_808
источник
4

Это решение основано на решениях @ FernandoEcheverria и @ GuilhermeMeireles, но оно более лаконично и работает с механизмами popstate, которые предоставляет Angular Router. Это позволяет сохранять и восстанавливать уровень прокрутки нескольких последовательных переходов.

Мы храним позиции прокрутки для каждого состояния навигации на карте scrollLevels. Как только есть событие popstate, идентификатор государства , который собирается быть восстановлен поставляются угловым Router: event.restoredState.navigationId. Затем он используется для получения последнего уровня прокрутки этого состояния scrollLevels.

Если для маршрута нет сохраненного уровня прокрутки, он будет прокручиваться вверх, как и следовало ожидать.

import { Component, OnInit } from '@angular/core';
import { Router, NavigationStart, NavigationEnd } from '@angular/router';

@Component({
    selector: 'my-app',
    template: '<ng-content></ng-content>',
})
export class AppComponent implements OnInit {

  constructor(private router: Router) { }

  ngOnInit() {
    const scrollLevels: { [navigationId: number]: number } = {};
    let lastId = 0;
    let restoredId: number;

    this.router.events.subscribe((event: Event) => {

      if (event instanceof NavigationStart) {
        scrollLevels[lastId] = window.scrollY;
        lastId = event.id;
        restoredId = event.restoredState ? event.restoredState.navigationId : undefined;
      }

      if (event instanceof NavigationEnd) {
        if (restoredId) {
          // Optional: Wrap a timeout around the next line to wait for
          // the component to finish loading
          window.scrollTo(0, scrollLevels[restoredId] || 0);
        } else {
          window.scrollTo(0, 0);
        }
      }

    });
  }

}
Саймон Мэтьюсон
источник
Потрясающие. Я должен был сделать немного индивидуальную версию для прокрутки div, а не окна, но это работало. Одно ключевое отличие было scrollTopпротив scrollY.
BBaysinger
4

В дополнение к идеальному ответу, предоставленному @Guilherme Meireles, как показано ниже, вы можете настроить свою реализацию, добавив плавную прокрутку, как показано ниже.

 import { Component, OnInit } from '@angular/core';
    import { Router, NavigationEnd } from '@angular/router';

    @Component({
        selector: 'my-app',
        template: '<ng-content></ng-content>',
    })
    export class MyAppComponent implements OnInit {
        constructor(private router: Router) { }

        ngOnInit() {
            this.router.events.subscribe((evt) => {
                if (!(evt instanceof NavigationEnd)) {
                    return;
                }
                window.scrollTo(0, 0)
            });
        }
    }

затем добавьте фрагмент ниже

 html {
      scroll-behavior: smooth;
    }

в ваши styles.css

Ифесиначи Брайан
источник
1

для iPhone / IOS Safari вы можете обернуть с помощью setTimeout

setTimeout(function(){
    window.scrollTo(0, 1);
}, 0);
tubbsy
источник
в моем случае это также требовало установки элемента обтекания страницы css; height: 100vh + 1px;
tububy
1

Привет, ребята, это работает для меня в angular 4. Вы просто должны сослаться на родителя, чтобы прокрутить изменения маршрутизатора`

layout.component.pug

.wrapper(#outlet="")
    router-outlet((activate)='routerActivate($event,outlet)')

layout.component.ts

 public routerActivate(event,outlet){
    outlet.scrollTop = 0;
 }`
Джонас Аргуэльес
источник
1
простите за лень, что я не потрудился изучать мопса, но вы можете перевести на HTML?
CodyBugstein
0

@ Фернандо Эчеверрия отлично! но этот код не работает в хэш-маршрутизаторе или ленивом маршрутизаторе. потому что они не вызывают изменения местоположения. можно попробовать это:

private lastRouteUrl: string[] = []
  

ngOnInit(): void {
  this.router.events.subscribe((ev) => {
    const len = this.lastRouteUrl.length
    if (ev instanceof NavigationEnd) {
      this.lastRouteUrl.push(ev.url)
      if (len > 1 && ev.url === this.lastRouteUrl[len - 2]) {
        return
      }
      window.scrollTo(0, 0)
    }
  })
}

Витт Бултер
источник
0

Использование самого Routerсебя вызовет проблемы, которые вы не можете полностью преодолеть, чтобы поддерживать постоянную работу браузера. На мой взгляд, лучший способ - просто использовать кастом directiveи позволить этому сбросить прокрутку по клику. Хорошая вещь об этом, то, что, если вы находитесь на том же самом, urlчто вы нажимаете, страница также будет прокручиваться вверх. Это соответствует нормальным веб-сайтам. Основное directiveможет выглядеть примерно так:

import {Directive, HostListener} from '@angular/core';

@Directive({
    selector: '[linkToTop]'
})
export class LinkToTopDirective {

    @HostListener('click')
    onClick(): void {
        window.scrollTo(0, 0);
    }
}

Со следующим использованием:

<a routerLink="/" linkToTop></a>

Этого будет достаточно для большинства вариантов использования, но я могу представить несколько проблем, которые могут возникнуть из этого:

  • Не работает universalиз-за использованияwindow
  • Небольшое влияние скорости на обнаружение изменений, потому что оно срабатывает при каждом нажатии
  • Нет способа отключить эту директиву

На самом деле это довольно легко преодолеть эти проблемы:

@Directive({
  selector: '[linkToTop]'
})
export class LinkToTopDirective implements OnInit, OnDestroy {

  @Input()
  set linkToTop(active: string | boolean) {
    this.active = typeof active === 'string' ? active.length === 0 : active;
  }

  private active: boolean = true;

  private onClick: EventListener = (event: MouseEvent) => {
    if (this.active) {
      window.scrollTo(0, 0);
    }
  };

  constructor(@Inject(PLATFORM_ID) private readonly platformId: Object,
              private readonly elementRef: ElementRef,
              private readonly ngZone: NgZone
  ) {}

  ngOnDestroy(): void {
    if (isPlatformBrowser(this.platformId)) {
      this.elementRef.nativeElement.removeEventListener('click', this.onClick, false);
    }
  }

  ngOnInit(): void {
    if (isPlatformBrowser(this.platformId)) {
      this.ngZone.runOutsideAngular(() => 
        this.elementRef.nativeElement.addEventListener('click', this.onClick, false)
      );
    }
  }
}

Это учитывает большинство сценариев использования, при том же использовании, что и основной, с преимуществом включения / выключения:

<a routerLink="/" linkToTop></a> <!-- always active -->
<a routerLink="/" [linkToTop]="isActive"> <!-- active when `isActive` is true -->

рекламные ролики, не читайте, если вы не хотите, чтобы вас рекламировали

Еще одно улучшение может быть сделано, чтобы проверить, поддерживает ли браузер passiveсобытия. Это немного усложнит код и будет немного неясным, если вы захотите реализовать все это в своих пользовательских директивах / шаблонах. Вот почему я написал небольшую библиотеку, которую вы можете использовать для решения этих проблем. Чтобы иметь ту же функциональность, что и выше, и с добавленным passiveсобытием, вы можете изменить свою директиву на эту, если вы используете ng-event-optionsбиблиотеку. Логика внутри click.pnbслушателя:

@Directive({
    selector: '[linkToTop]'
})
export class LinkToTopDirective {

    @Input()
    set linkToTop(active: string|boolean) {
        this.active = typeof active === 'string' ? active.length === 0 : active;
    }

    private active: boolean = true;

    @HostListener('click.pnb')
    onClick(): void {
      if (this.active) {
        window.scrollTo(0, 0);
      }        
    }
}
Пол Круйт
источник
0

Это сработало для меня лучше всего для всех изменений навигации, включая хэш-навигацию

constructor(private route: ActivatedRoute) {}

ngOnInit() {
  this._sub = this.route.fragment.subscribe((hash: string) => {
    if (hash) {
      const cmp = document.getElementById(hash);
      if (cmp) {
        cmp.scrollIntoView();
      }
    } else {
      window.scrollTo(0, 0);
    }
  });
}
Йорг Янке
источник
0

Основная идея этого кода - сохранить все посещенные URL вместе с соответствующими данными scrollY в массиве. Каждый раз, когда пользователь покидает страницу (NavigationStart), этот массив обновляется. Каждый раз, когда пользователь входит на новую страницу (NavigationEnd), мы решаем восстановить позицию Y или нет, в зависимости от того, как мы попадаем на эту страницу. Если использовалась ссылка на какой-либо странице, мы переходим к 0. Если были использованы функции браузера назад / вперед, мы переходим к Y, сохраненному в нашем массиве. Извините за мой английский :)

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Location, PopStateEvent } from '@angular/common';
import { Router, Route, RouterLink, NavigationStart, NavigationEnd, 
    RouterEvent } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';

@Component({
  selector: 'my-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {

  private _subscription: Subscription;
  private _scrollHistory: { url: string, y: number }[] = [];
  private _useHistory = false;

  constructor(
    private _router: Router,
    private _location: Location) {
  }

  public ngOnInit() {

    this._subscription = this._router.events.subscribe((event: any) => 
    {
      if (event instanceof NavigationStart) {
        const currentUrl = (this._location.path() !== '') 
           this._location.path() : '/';
        const item = this._scrollHistory.find(x => x.url === currentUrl);
        if (item) {
          item.y = window.scrollY;
        } else {
          this._scrollHistory.push({ url: currentUrl, y: window.scrollY });
        }
        return;
      }
      if (event instanceof NavigationEnd) {
        if (this._useHistory) {
          this._useHistory = false;
          window.scrollTo(0, this._scrollHistory.find(x => x.url === 
          event.url).y);
        } else {
          window.scrollTo(0, 0);
        }
      }
    });

    this._subscription.add(this._location.subscribe((event: PopStateEvent) 
      => { this._useHistory = true;
    }));
  }

  public ngOnDestroy(): void {
    this._subscription.unsubscribe();
  }
}
Владимир Турыгин
источник
0

window.scrollTo()не работает для меня в Angular 5, поэтому я использовал document.body.scrollTopкак,

this.router.events.subscribe((evt) => {
   if (evt instanceof NavigationEnd) {
      document.body.scrollTop = 0;
   }
});
Рохан Кумар
источник
0

Если вы загружаете разные компоненты по одному и тому же маршруту, вы можете использовать ViewportScroller для достижения того же результата.

import { ViewportScroller } from '@angular/common';

constructor(private viewportScroller: ViewportScroller) {}

this.viewportScroller.scrollToPosition([0, 0]);
Сэнди
источник
0

Окно прокрутки вверх.
Window.pageYOffset и document.documentElement.scrollTop возвращают одинаковый результат во всех случаях. window.pageYOffset не поддерживается ниже IE 9.

app.component.ts

import { Component, HostListener, ElementRef } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  isShow: boolean;
  topPosToStartShowing = 100;

  @HostListener('window:scroll')
  checkScroll() {

    const scrollPosition = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;

    console.log('[scroll]', scrollPosition);

    if (scrollPosition >= this.topPosToStartShowing) {
      this.isShow = true;
    } else {
      this.isShow = false;
    }
  }

  gotoTop() {
    window.scroll({ 
      top: 0, 
      left: 10, 
      behavior: 'smooth' 
    });
  }
}

app.component.html

<style>
  p {
  font-family: Lato;
}

button {
  position: fixed;
  bottom: 5px;
  right: 5px;
  font-size: 20px;
  text-align: center;
  border-radius: 5px;
  outline: none;
}
  </style>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>
<p>
  Lorem ipsum dolor sit, amet consectetur adipisicing elit. Minus, repudiandae quia. Veniam amet fuga, eveniet velit ipsa repudiandae nemo? Sit dolorem itaque laudantium dignissimos, rerum maiores nihil ad voluptates nostrum.
</p>

<button *ngIf="isShow" (click)="gotoTop()">👆</button>
Йогеш Вагмаре
источник