August 21, 2017

Scroll to item-of-interest in Angular

Imagine you have a list of elements of some kind (by means of *ngFor). You'd like to make that Component aware of the fragment in the URL so it can scroll to the element identified by it.

For instance, you let the user create a new element in a form in one page and you want that on return, the list scrolls automatically to the item just added. You are using Angular routing facilities.

For more background, you can check the discussion on the issue on the Angular project.

There, some users have provided their approaches. On the basis of some solutions suggested there, I've built my own and it seems to work fine:

[...]

private fragment: string;

constructor(private route: ActivatedRoute { }

ngOnInit() {
this.route.fragment.subscribe(fragment => { this.fragment = fragment; });
}

ngAfterViewChecked(): void {
try {
document.querySelector('#' + this.fragment).scrollIntoView();
} catch (e) { }
}

[...]

I think this adds very little bloat and it handles these situations:

  • No fragment present in the route.
  • Fragment present but invalid (querySelector() throws).
  • Fragment present and valid, but the element is not present.

Actually, the try block is masking these situations rather than handling them. Nevertheless, usually you won't find yourself needing to do anything about them.

A little improvement

This will work as long as there is not a sticky heading. In that case, since the browser will align the top of the item with the top of the viewport, almost every item but those at the bottom as subject to get covered by the heading.

A simple workaround, which also improves the experience regardless a sticky heading is used or not, is adding this line after the first scroll call:

window.scrollBy(null, -window.innerHeight / 2);

With that you will be approximately centering the item vertically, which also will help in avoiding the covering. Of course, it depends on both the height of the element and the sticky heading, but for most cases it does the trick.

A caveat

This is for "abrupt" scrolling, which is the most common use case for navigating back to the list. If you needed fancier scrolling, you'd need to refine the code to get the element position and compensate for the heading size to be able to make only one scroll call with whatever framework you are using. Or you can use a third-party library that already implements that.

In any case, this is how you instruct your app to automatically scroll to an item-of-interest.

No comments:

Post a Comment