web-dev-qa-db-fra.com

jquery - empêche la fenêtre de changer la position du défilement lors de l'ajout d'éléments à une liste?

J'ai une page qui affiche des messages et je veux qu'elle fonctionne comme Facebook, mais sans le chargeur paresseux. Les messages sont affichés par ordre chronologique, le plus récent en dernier.

Ma liste de messages est initialement remplie x nombre des messages les plus récents, et la fenêtre défile vers le bas. Lorsque l'utilisateur commence à lire le fil, il lit de bas en haut. S'ils arrivent au sommet, ils peuvent charger plus de messages ... Je les oblige à cliquer sur un bouton ... Facebook a un chargeur paresseux. Les nouveaux messages sont ajoutés à la liste.

Problème: à mesure que les nouveaux messages sont ajoutés au début de la liste, les messages existants sont enfoncés, ce qui entraîne la perte de l’emplacement de visualisation. Comment puis-je conserver la position d'affichage actuelle de l'utilisateur lorsque j'ajoute les nouveaux messages? Par exemple, ouvrez un fil de messages long dans facebook, faites défiler vers le haut, ce qui entraîne l'ajout de nouveaux messages ... l'emplacement de votre affichage ne change pas, même si la position de défilement change.

29
Redtopia

Stockez une référence au premier message avant de préparer les nouveaux messages, puis définissez le défilement sur le décalage du 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/

49
Jeff B

Pas besoin de jQuery ici, il suffit de vérifier les modifications 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 l'accès à un tableau. c'est à dire:

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

Certaines personnes qui viennent ici peuvent simplement vouloir ajouter du contenu sans que le focus actuel ne saute aux yeux. La modification de la démo de Jeff B ci-dessus pour utiliser la cible d'événements de clic 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] en un point quelconque de la page).

3
ericP

Voici un truc simple qui a fonctionné pour moi:

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

Vous pouvez également conserver la position de défilement en fonction du décalage au 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
Dany Gauthier

Je viens de croiser une variante de ceci. J'ai détaché un grand nombre d'éléments, les ai traités et les ai ensuite rattachés. cela ne se termine pas de manière synchrone dans Chrome, je ne pouvais donc pas définir $ el.scrollTop (oldval) de manière fiable. J'ai trouvé cette solution qui fonctionnait 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
xeo