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.

25
demandé sur Redtopia 2012-03-23 08:22:42

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

Démo: http://jsfiddle.net/GRnQY/5/

44
répondu Jeff B 2012-03-23 04:54:11

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

Http://jsfiddle.net/fkqqv28e/

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;
6
répondu oodavid 2015-11-25 14:15:49

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).

2
répondu ericP 2016-05-14 06:25:19

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);
}
0
répondu xeo 2016-02-20 17:29:18

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);
0
répondu Dany Gauthier 2016-09-13 18:49:28

Voici une astuce simple qui a fonctionné pour moi:

// divWithTheScroll.prependElement...
divWithTheScroll.scrollTop += newlyPrependedElement.scrollHeight;
0
répondu Lacho Tomov 2018-09-09 17:06:35