Automatic scrolling, only if a user already scrolled the bottom of a page in Angular
In chat windows or similar growing lists, users often expect, that a page automatically scrolls to the latest item. That behavior obviously should get stopped, when a user scrolls through the list and is not viewing that latest item currently. Here is how to achieve that in Angular.
In chat windows or similar growing lists, users often expect, that a page automatically scrolls to the latest item. That behavior obviously should get stopped, when a user scrolls through the list and is not viewing that latest item currently. Here is how to achieve that in Angular.
If you are only interested in the final code, you can find the full sample at StackBlitz, to play around with it.
Here is a quick video, demonstrating, what we are trying to achieve:
First, we need a list of items, an HTML element to display these items in and a method to call for scrolling to the bottom automatically. An Angular component that contains all that can look like this:
<!-- app.component.html -->
<div class="frame" #scrollframe>
<div *ngFor="let item of items" class="item" #item>
Item
</div>
</div>
// app.component.ts
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements AfterViewInit {
@ViewChild('scrollframe', {static: false}) scrollFrame: ElementRef;
@ViewChildren('item') itemElements: QueryList<any>;
private scrollContainer: any;
private items = [];
ngAfterViewInit() {
this.scrollContainer = this.scrollFrame.nativeElement;
this.itemElements.changes.subscribe(_ => this.onItemElementsChanged());
// Add a new item every 2 seconds
setInterval(() => {
this.items.push({});
}, 2000);
}
private onItemElementsChanged(): void {
this.scrollToBottom();
}
private scrollToBottom(): void {
this.scrollContainer.scroll({
top: this.scrollContainer.scrollHeight,
left: 0,
behavior: 'smooth'
});
}
}
As you can see, we defined the container element in which we want to perform the scrolling operation. The code above lets the view automatically scroll down to the bottom, if the itemElements
list changes.
Only scroll automatically, if user has already scrolled to the end
Usually, we only want to scroll automatically, if the user is already viewing the last element the bottom. Otherwise we should remain still. So we need a function to tell us, if the user's current scroll position is near the bottom of the screen.
private isUserNearBottom(): boolean {
const threshold = 150;
const position = this.scrollContainer.scrollTop + this.scrollContainer.offsetHeight;
const height = this.scrollContainer.scrollHeight;
return position > height - threshold;
}
Let's add a new isNearBottom
variable, which we can set, whenever the user scrolled within the scrollContainer
element. For this, we need to subscribe to the scroll
event in the HTML element.
<div class="frame" (scroll)="scrolled($event)" #scrollframe>
<!-- ... -->
</div>
Within the component, we need to react on this event and update the isNearBottom
variable.
scrolled(event: any): void {
this.isNearBottom = this.isUserNearBottom();
}
With this in place, we can update the onItemElementsChanged()
method, to only scroll down, if the user's current scroll position is near bottom.
private onItemElementsChanged(): void {
if (this.isNearBottom()) {
this.scrollToBottom();
}
}
What if I want to scroll a full window?
If you want to scroll the full window instead of the content of a single div
, the scrolling code looks slightly different:
private scrollToBottom(): void {
window.scroll({ // <- Scroll window instead of scrollContainer
top: this.scrollContainer.scrollHeight,
left: 0,
behavior: 'smooth'
});
}
private isUserNearBottom(): boolean {
const threshold = 150;
const position = window.scrollY + window.innerHeight; // <- Measure position differently
const height = document.body.scrollHeight; // <- Measure height differently
return position > height - threshold;
}
@HostListener('window:scroll', ['$event']) // <- Add scroll listener to window
scrolled(event: any): void {
this.isNearBottom = this.isUserNearBottom();
}
Full example
You can find the full sample at StackBlitz, to play around with it.
☝️ Advertisement Block: I will buy myself a pizza every time I make enough money with these ads to do so. So please feed a hungry developer and consider disabling your Ad Blocker.