jQuery-empêcher la fenêtre de changer la position de défilement tout en ajoutant des éléments à une liste?
J'ai une page qui affiche des messages et je veux que cela fonctionne comme Facebook, mais sans le chargeur paresseux. Les Messages sont affichés dans l'ordre chronologique, le plus récent dernier.
Ma liste de messages est initialement remplie x nombre de messages les plus récents, et la fenêtre défile vers le bas. Lorsque l'utilisateur commence à lire le fil, ils lisent de bas en haut. S'ils arrivent au sommet, ils peuvent charger plus de messages... Je leur fais cliquer sur un bouton... facebook a un chargeur paresseux. Nouveau les messages sont ajoutés à la liste.
Problème: au fur et à mesure que les nouveaux messages sont ajoutés, les messages existants sont poussés vers le bas, ce qui fait que l'utilisateur perd sa place de "visualisation". Comment puis-je conserver la position de vue actuelle de l'utilisateur lorsque j'Ajoute les nouveaux messages? Par exemple, ouvrez un long fil de message dans facebook, Faites défiler vers le haut provoquant l'ajout de nouveaux messages... votre emplacement de vue ne change pas même si la position de défilement le fait.
6 réponses
Stockez une référence au premier message avant d'ajouter les nouveaux messages, et après avoir ajouté, définissez le défilement sur le décalage de ce 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);
}
});
Démo: http://jsfiddle.net/GRnQY/
Edit : j'ai remarqué que vous le vouliez avec un bouton. Vous devrez peut-être faire un peu plus de maths:
$(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);
});
Pas besoin de jQuery ici, il suffit de vérifier les changements dans les éléments scrollHeight et d'ajuster le scrollTop en conséquence, c'est-à-dire:
var lastScrollHeight = el.scrollHeight;
for(var n=0; n<10; n++){
// Prepend here...
}
var scrollDiff = el.scrollHeight - lastScrollHeight;
el.scrollTop += scrollDiff;
Démo
JQuery
Pour accéder au nœud DOM natif à partir d'une collection jQuery, utilisez 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;
Certaines personnes qui viennent ici peuvent simplement vouloir ajouter du contenu sans que le focus actuel ne saute. Modifier la démo de Jeff B ci-dessus pour utiliser la cible de l'événement click rend cet idiome plus 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/ (variante de la démo de Jeff B ci-dessus mais avec le bouton [Ajouter plus] à un point arbitraire dans la page).
Je viens de rencontrer une variante de ceci. je l'ai détaché un grand nombre d'éléments, traitées, puis attaché de nouveau. cela ne se termine pas de manière synchrone dans Chrome, donc je ne pouvais pas définir $el.scrollTop (oldval) fiable. J'ai trouvé cette solution a fonctionné pour moi.
// 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);
}
Vous pouvez également conserver la position de défilement en fonction du décalage vers le bas de la page/de l'élément.
var ScrollFromBottom = $(document).height() - $(window).scrollTop();
//ajax - add messages -- geenrate html code then add to dom
//set scroll position
$(window).scrollTop($(document).height() - ScrollFromBottom);
Voici une astuce simple qui a fonctionné pour moi:
// divWithTheScroll.prependElement...
divWithTheScroll.scrollTop += newlyPrependedElement.scrollHeight;