Loading more results (infinite scroll) causes scrollbar to go havoc


#1

I have a very weird problem.

I got an infinite scrolling system for my collection, where it increases the limit when you reach the bottom of the window. However, for some reason it brings the scrollbar to the top when you reach the bottom, and the entire collection gets reloaded (or so it seems).

(client)

Code
Template.indexTopics.helpers({
    topics: function () {
        var cursor = Topics.find({});
        cursor.sorter = {
            getComparator: function() {
                return function(a, b) {
                    if (a.starred) return (b.starred) ? 0 : -1;
                    if (b.starred) return 1;
                    return b.createdAt - a.createdAt;
                }
            }
        };

        return cursor;
    }
});

lastScrollTop = 0;

$(window).scroll(function(event){
    var CurrentPos = $(window).scrollTop() + $(window).height();
    if(CurrentPos > $(document).height() - 100) {
        var scrollTop = $(this).scrollTop();

        if(scrollTop > lastScrollTop) {
            Session.set('itemsLimit', Session.get('itemsLimit') + 10);
        }

        lastScrollTop = scrollTop;
    }
});

I tried solving it by using

$('html,body').animate({
 scrollTop: CurrentPos},
'fast');

but it doesn’t really help.

Is there any way I can make it just add more results, without affecting the position of the scrollbar itself? (yes, I know it will get smaller when the body height increases, but that’s not what I’m talking about)


#2

Correct me if I am wrong, but I think what may be happening here is that you have a single subscription for your result set which you wait for it to be ready before rendering the results to the page.

In this case when you increase the limit the ready status becomes false, the whole result set is removed from the page causing the scroll position to be reset and then the status becomes ready again and the results are rendered to the page again.


#3

@copleykj I see, thanks for the input.

So, if I got this right, then I could just simply force it to be ready when it stops?

Deps.autorun(function() {
    Meteor.subscribe('mycollections', Session.get('itemsLimit'), {
        onStop: function () {
            this.ready();
        },
    });
});

Although, that doesn’t work, as it says that this.ready is not defined.

Do you have any suggestions on how I could approach the issue?


#4

The exact details will vary depending on which UI library you are using and how you have architected your app, but basically what you’ll want is an initial subscription for the first bit of data. That will be subscription that you check the ready status of, and it shouldn’t change once it becomes ready. Then you would subscribe to the rest of the data in multiple other subscriptions passing them a limit and a skip.


#5

Now I understand what you meant, thanks.


#6

Just to add to @copleykj’s excellent explanation, you should ensure you sort the published data in such a way that “old” entries do not change position - typically order by create date - ascending or descending, depending on your use case.


#7

I’m already sorting it in my publication.

Thanks for all the help, I finally got it to work!