メッセージを表示するページがあり、遅延ローダーを使用せずにFacebookと同じように機能させたい。メッセージは発生順に表示され、最新のものから順に表示されます。
私のメッセージリストには、最初に最新のメッセージ数xが入力され、ウィンドウが一番下までスクロールされます。ユーザーがスレッドの読み取りを開始すると、下から上に読み取ります。彼らがトップに到達した場合、彼らはより多くのメッセージをロードすることができます...私は彼らにボタンをクリックさせます... facebookには遅延ローダーがあります。新しいメッセージはリストの先頭に追加されます。
問題:新しいメッセージが先頭に追加されると、既存のメッセージが押し下げられ、ユーザーが「表示」場所を失う原因になります。新しいメッセージを追加するときに、ユーザーの現在のビュー位置を維持するにはどうすればよいですか?例として、Facebookで長いメッセージスレッドを開き、上部にスクロールして新しいメッセージを追加します...スクロール位置が変わってもビューの位置は変わりません。
新しいメッセージを付加する前に最初のメッセージへの参照を保存し、付加した後、スクロールをそのメッセージのオフセットに設定します。
$(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);
});
ここではjQueryは必要ありません。要素scrollHeightの変更を確認し、それに応じてscrollTopを調整します。つまり、
var lastScrollHeight = el.scrollHeight;
for(var n=0; n<10; n++){
// Prepend here...
}
var scrollDiff = el.scrollHeight - lastScrollHeight;
el.scrollTop += scrollDiff;
デモ
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;
ここに来る一部の人々は、現在のフォーカスを飛び越えずにコンテンツを追加したいだけかもしれません。上記の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のデモのバリアントですが、ページの任意の場所に[さらに追加]ボタンがあります)。
ここに私のために働いた簡単なトリックがあります:
// divWithTheScroll.prependElement...
divWithTheScroll.scrollTop += newlyPrependedElement.scrollHeight;
ページ/要素の下部へのオフセットに基づいてスクロール位置を維持することもできます。
var ScrollFromBottom = $(document).height() - $(window).scrollTop();
//ajax - add messages -- geenrate html code then add to dom
//set scroll position
$(window).scrollTop($(document).height() - ScrollFromBottom);
私はこれのバリエーションに遭遇しました。多数の要素を切り離し、処理してから、再度取り付けました。これは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);
}