javascriptjqueryangularinfinite-scrollintersection-observer

Angular Infinite Scroll with sorted list results in endless loop


Description

I'm working on an Angular project. I implemented an "infinite scroll" list utilizing IntersectionObvserver. Generally it works quite well and satisfying.

The entries to build the list are being sorted ascending.

Problem

In Angular this causes the problem that the HTML list is built upwards while the scrollbar remains at the same position that triggered the infinite scroll event, which causes the event to fire over and over and over again until the user scrolls away manually.

That the event is being fired as long as the infinite scroll trigger is in the viewport is desired behavior, because depending on the viewport height and user preferences it can't be guaranteed that there will always be enough items to fill the whole page initially. New items must be loaded continously until the page is filled.

StackBlitz example

https://stackblitz.com/edit/angular-infinite-scroll-intersectionobserver

Scroll to the very end of the list to trigger the infinite scroll event. It will keep the scrollbar at the same position and firing the event while building the list upwards.

List is built upwards and the scrollbar remains at the same position

In the generateItems() function there is a call to sortItems(). When you disable this call the scrolling behavior of the infinite scroll list works properly. New items added to the list cause the list to grow and push the infinite scroll trigger out of the viewport.

CodePen example

When I implement the same infinite scroll logic using native JavaScript with jQuery it works as expected, even when the results are sorted.

https://codepen.io/mw-108/pen/ExqPKmG

Here the infinite scroll trigger is always pushed out of the viewport when the list is updated. This is what I expect.

Questions

Am I doing something wrong in Angular?

What can I do to reproduce the same result I see in the JavaScript / jQuery example in Angular?


Solution

  • I am not sure why it is working on the jQuery but I actually found the problem and a solution for that. the issue is happening because the sortItems function is directly modifying the original array and it is re-rendering the whole array in template.

    So instead I modified the sorting function as below, what is does is that it return a cloned copy to the original array and reassigns it (instead of modifying it).

    private sortItems() {
      return this.items.slice(0).sort((a, b) => a.index - b.index);
    }
    

    If you want more information on mutation you can refer this answer