web-dev-qa-db-ja.com

jquery-アイテムをリストの先頭に追加するときにウィンドウがスクロール位置を変更しないようにしますか?

メッセージを表示するページがあり、遅延ローダーを使用せずにFacebookと同じように機能させたい。メッセージは発生順に表示され、最新のものから順に表示されます。

私のメッセージリストには、最初に最新のメッセージ数xが入力され、ウィンドウが一番下までスクロールされます。ユーザーがスレッドの読み取りを開始すると、下から上に読み取ります。彼らがトップに到達した場合、彼らはより多くのメッセージをロードすることができます...私は彼らにボタンをクリックさせます... facebookには遅延ローダーがあります。新しいメッセージはリストの先頭に追加されます。

問題:新しいメッセージが先頭に追加されると、既存のメッセージが押し下げられ、ユーザーが「表示」場所を失う原因になります。新しいメッセージを追加するときに、ユーザーの現在のビュー位置を維持するにはどうすればよいですか?例として、Facebookで長いメッセージスレッドを開き、上部にスクロールして新しいメッセージを追加します...スクロール位置が変わってもビューの位置は変わりません。

35
Redtopia

新しいメッセージを付加する前に最初のメッセージへの参照を保存し、付加した後、スクロールをそのメッセージのオフセットに設定します。

$(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);
    }
});

デモ: http://jsfiddle.net/GRnQY/

編集:私はあなたがボタンでそれを望んでいることに気づきました。あなたはもう少し数学をしなければならないかもしれません:

$(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);
});

デモ: http://jsfiddle.net/GRnQY/5/

56
Jeff B

ここではjQueryは必要ありません。要素scrollHeightの変更を確認し、それに応じてscrollTopを調整します。つまり、

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

デモ

http://jsfiddle.net/fkqqv28e/

jQuery

JQueryコレクションからネイティブDOMノードにアクセスするには、配列アクセスを使用します。すなわち:

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;
12
oodavid

ここに来る一部の人々は、現在のフォーカスを飛び越えずにコンテンツを追加したいだけかもしれません。上記のJeff Bのデモを変更してクリックイベントターゲットを使用すると、このイディオムがより移植性の高いものになります。

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/ (上記のJeff Bのデモのバリアントですが、ページの任意の場所に[さらに追加]ボタンがあります)。

3
ericP

ここに私のために働いた簡単なトリックがあります:

// divWithTheScroll.prependElement...
divWithTheScroll.scrollTop += newlyPrependedElement.scrollHeight;
1
Lacho Tomov

ページ/要素の下部へのオフセットに基づいてスクロール位置を維持することもできます。

var ScrollFromBottom = $(document).height() - $(window).scrollTop();

//ajax - add messages -- geenrate html code then add to dom

//set scroll position
$(window).scrollTop($(document).height() - ScrollFromBottom);
0
Dany Gauthier

私はこれのバリエーションに遭遇しました。多数の要素を切り離し、処理してから、再度取り付けました。これはChromeで同期的に完了しないため、$ el.scrollTop(oldval)を確実に設定できませんでした。この解決策がうまくいくことがわかりました。

// get old scroll top of '#sub_elem'
var oldScrollTop = $('#sub_elem').scrollTop();

// detach
var $els = $('#sub_elem').detach();

// complex processing on #sub_elem
// goes here

// attach
$('#main_el').attach($els);

// problem- always scrolls #sub_elem back to the top
// attempt #1: $('#sub_elem').scrollTop(oldScrollTop); // fails on mac Chrome
// attempt #2: use instant animation.  this works on mac Chrome
if (oldScrollTop) {
    $('#sub_elem').animate({
        scrollTop: oldScrollTop
    }, 0);
}
0
xeo