Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jquery - keep window from changing scroll position while prepending items to a list?

I have a page that displays messages and I want it to work just like Facebook, but without the lazy loader. Messages are displayed in chronological order, most recent last.

My message list is initially populated x number of most recent messages, and the window is scrolled to the bottom. When the user starts to read the thread, they read from bottom to top. If they get to the top, they can load more messages... I make them click a button... facebook has a lazy loader. New messages are prepended to the list.

Problem: As the new messages are prepended, the existing messages are pushed down, causing the user to lose their "viewing" place. How can I keep the user's current view position as I add the new messages? For an example, open a long message thread in facebook, scroll to the top causing new messages to be added... your view location doesn't change even though the scroll position does.

like image 258
Redtopia Avatar asked Mar 23 '12 04:03

Redtopia


People also ask

What is $( window scrollTop ()?

$(selector).scrollTop(position) Parameter. Description. position. Specifies the vertical scrollbar position in pixels.

Is jQuery scrollable?

jQuery. scrollable manages animated scrolling in windows, scrollable elements and iframes. It frees you from handling gotchas and edge cases and offers convenient, flexible options for animation. If you are a happy user of this project already, you can support its development by donating to it.


3 Answers

Store a reference to the first message before you prepend new messages, and after you prepend, set the scroll to the offset of that message:

$(document).on('scroll', function() {
    var scroll = $(document).scrollTop();
    if (scroll < 1) {
        // Store eference to first message
        var firstMsg = $('.message:first');

        // Prepend new message here (I'm just cloning...)
        $('body').prepend(firstMsg.clone());

        // After adding new message(s), set scroll to position of
        // what was the first message
        $(document).scrollTop(firstMsg.offset().top);
    }
});

Demo: http://jsfiddle.net/GRnQY/

Edit: I noticed you wanted it with a button. You might have to do a little more math:

$(document).on('click', '#loadMore', function() {
    var firstMsg = $('.message:first');

    // Where the page is currently:
    var curOffset = firstMsg.offset().top - $(document).scrollTop();

    // Prepend
    firstMsg.before(firstMsg.clone());

    // Offset to previous first message minus original offset/scroll
    $(document).scrollTop(firstMsg.offset().top-curOffset);
});

Demo: http://jsfiddle.net/GRnQY/5/

like image 104
Jeff B Avatar answered Oct 01 '22 13:10

Jeff B


No need for jQuery here, just check for changes in the elements scrollHeight and adjust the scrollTop accordingly, ie:

var lastScrollHeight = el.scrollHeight;
for(var n=0; n<10; n++){
    // Prepend here...
}
var scrollDiff = el.scrollHeight - lastScrollHeight;
el.scrollTop += scrollDiff;

Demo

http://jsfiddle.net/fkqqv28e/

jQuery

To access the native DOM node from a jQuery collection, use array access; ie:

var lastScrollHeight = $('#myElement')[0].scrollHeight;
for(var n=0; n<10; n++){
    $('#myElement').prepend('<div>prepended '+Math.random()+'</div>');
}
var scrollDiff = $('#myElement')[0].scrollHeight - lastScrollHeight;
$('#myElement')[0].scrollTop += scrollDiff;
like image 24
oodavid Avatar answered Oct 01 '22 14:10

oodavid


Some folks coming here may just want to add content without the current focus jumping around. Modifying Jeff B's demo above to use the click event target makes this idiom more portable:

someButton.on('click', function() {
    var curOffset = $(this).offset().top - $(document).scrollTop();
    // add content to your heart's content.
    $(document).scrollTop($(this).offset().top-curOffset);
});

c.f. http://jsfiddle.net/bwz8uLvt/1/ (variant of Jeff B's demo above but with the [add more] button at an arbitrary point in the page).

like image 7
ericP Avatar answered Oct 01 '22 15:10

ericP