jquery - удерживать окно от изменения позиции прокрутки при добавлении элементов в список?
У меня есть страница, которая отображает сообщения, и я хочу, чтобы она работала так же, как Facebook, но без ленивого загрузчика. Сообщения отображаются в хронологическом порядке, самые последние последние.
Мой список сообщений изначально заполняется x числом самых последних сообщений, и окно прокручивается вниз. Когда пользователь начинает читать поток, он читает снизу вверх. Если они доберутся до вершины, они смогут загрузить больше сообщений... Я заставляю их нажимать кнопку... У фейсбука есть ленивый загрузчик. Новые сообщения добавляются в список.
Проблема: по мере добавления новых сообщений существующие сообщения выталкиваются вниз, в результате чего пользователь теряет свое место "просмотра". Как сохранить текущую позицию просмотра пользователя при добавлении новых сообщений? Например, откройте длинную ветку сообщений в Facebook, прокрутите вверх, вызывая добавление новых сообщений... ваше местоположение просмотра не меняется, даже если позиция прокрутки меняется.
7 ответов
Сохраните ссылку на первое сообщение перед тем, как добавлять новые сообщения, а после добавления установите прокрутку на смещение этого сообщения:
$(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
Чтобы получить доступ к собственному узлу DOM из коллекции jQuery, используйте доступ к массиву; то есть:
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;
Некоторые люди, приезжающие сюда, могут просто захотеть добавить контент, не обращая внимания на текущий фокус. Изменение вышеприведенной демонстрации Джеффа Б для использования цели события click делает эту идиому более переносимой:
someButton.on('click', function() {
var curOffset = $(this).offset().top - $(document).scrollTop();
// add content to your heart's content.
$(document).scrollTop($(this).offset().top-curOffset);
});
cf http://jsfiddle.net/bwz8uLvt/1/ (вариант демонстрации Джеффа 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);
Немного поздно. Если вы ориентируетесь на современные браузеры, вы должны использоватьElement.scrollIntoView()
Метод scrollIntoView() интерфейса Element прокручивает родительский контейнер элемента так, чтобы элемент, для которого вызывается scrollIntoView(), был виден пользователю.
var element = document.getElementById("box");
element.scrollIntoView();
element.scrollIntoView(false);
element.scrollIntoView({block: "end"});
element.scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"});
Я просто наткнулся на вариант этого. Я отсоединил большое количество элементов, обработал их, а затем снова прикрепил их. это не завершается синхронно в Chrome, поэтому я не смог установить $el.scrollTop(oldval) надежно. Я нашел это решение работает для меня.
// get old scroll top of '#sub_elem'
oldScrollTop = $('#sub_elem).scrollTop();
// detach
$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);
}