Skip to content

Instantly share code, notes, and snippets.

@Londeren
Last active August 5, 2022 08:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Londeren/d3c3b45bcf32dad98396d29b25c5347c to your computer and use it in GitHub Desktop.
Save Londeren/d3c3b45bcf32dad98396d29b25c5347c to your computer and use it in GitHub Desktop.
Angular 14 custom ViewportScroller for Router option scrollPositionRestoration

Angular 14 custom ViewportScroller for Router option scrollPositionRestoration

Problem. Router option scrollPositionRestoration: 'enabled|top' doesn't work.

Also see the issue.

// …
@NgModule({
imports: [
AppRouterModule,
// …
],
bootstrap: [RootComponent],
providers: [
// …
{
provide: ViewportScroller,
useFactory: () => new CustomViewportScroller('#main', inject(DOCUMENT), inject(ErrorHandler))
}
]
})
export class AppModule {}
import { ViewportScroller } from '@angular/common';
import { ErrorHandler } from '@angular/core';
export class CustomViewportScroller implements ViewportScroller {
private offset: () => [number, number] = () => [0, 0];
private readonly window: Window;
constructor(
private readonly scrollElementSelector: string,
private readonly document: Document,
private readonly errorHandler: ErrorHandler
) {
this.window = this.document.defaultView;
}
public setOffset(offset: [number, number] | (() => [number, number])): void {
if (Array.isArray(offset)) {
this.offset = () => offset;
} else {
this.offset = offset;
}
}
public getScrollPosition(): [number, number] {
const scrollEl = this.document.querySelector(this.scrollElementSelector);
if (this.supportsScrolling() && scrollEl) {
return [scrollEl.scrollLeft, scrollEl.scrollTop];
}
return [0, 0];
}
public scrollToPosition(position: [number, number]): void {
const scrollEl = this.document.querySelector(this.scrollElementSelector);
if (this.supportsScrolling() && scrollEl) {
scrollEl.scrollTo(...position);
}
}
public scrollToAnchor(anchor: string): void {
if (!this.supportsScrolling()) {
return;
}
try {
const elSelectedById = this.document.querySelector(`#${anchor}`);
if (elSelectedById) {
this.scrollToElement(elSelectedById);
return;
}
const elSelectedByName = this.document.querySelector(`[name='${anchor}']`);
if (elSelectedByName) {
this.scrollToElement(elSelectedByName);
return;
}
} catch (e) {
this.errorHandler.handleError(e);
}
}
public setHistoryScrollRestoration(scrollRestoration: 'auto' | 'manual'): void {
if (this.supportsScrolling()) {
return;
}
const history = this.window.history;
if (history && history.scrollRestoration) {
history.scrollRestoration = scrollRestoration;
}
}
private scrollToElement(el: Element): void {
const rect = el.getBoundingClientRect();
const left = rect.left + this.window.scrollX;
const top = rect.top + this.window.scrollY;
const [offsetX, offsetY] = this.offset();
this.window.scrollTo(left - offsetX, top - offsetY);
}
private supportsScrolling(): boolean {
try {
return !!this.window && !!this.window.scrollTo;
} catch {
return false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment