2012-03-23 22 views
22

Tengo una página que muestra mensajes y quiero que funcione como Facebook, pero sin el cargador lento. Los mensajes se muestran en orden cronológico, el último más reciente.jquery: ¿evitar que la ventana cambie la posición de desplazamiento mientras antepone elementos a una lista?

Mi lista de mensajes está poblada inicialmente un número x de la mayoría de los mensajes recientes, y la ventana se desplaza a la parte inferior. Cuando el usuario comienza a leer el hilo, lo leen de abajo hacia arriba. Si llegan a la cima, pueden cargar más mensajes ... Les hago clic en un botón ... Facebook tiene un cargador lento. Los mensajes nuevos se anteponen a la lista.

Problema: A medida que se anteponen los nuevos mensajes, los mensajes existentes son empujados hacia abajo, haciendo que el usuario pierda su "visión" lugar. ¿Cómo puedo mantener la posición de vista actual del usuario mientras agrego los mensajes nuevos? Por ejemplo, abra una cadena larga de mensajes en Facebook, desplácese hasta la parte superior y haga que se agreguen nuevos mensajes ... la ubicación de su vista no cambia aunque la posición de desplazamiento sí lo haga.

Respuesta

41

almacenar una referencia al primer mensaje antes de antepones nuevos mensajes, y después de antepones, establecer el desplazamiento para el desplazamiento de ese mensaje:

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

Demostración: http://jsfiddle.net/GRnQY/

Editar: Me di cuenta de que lo querías con un botón. Puede que tenga que hacer un poco más de matemáticas:

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

Demostración: http://jsfiddle.net/GRnQY/5/

+6

La segunda solución es IMPRESIONANTE - muchas gracias, esto fue muy útil para implementar un "desplazamiento infinito inverso" al estilo de los mensajes de Facebook. –

+0

Si está usando ajax, agregue '$ (selector) .scrollTop (firstMsg.offset(). Top-curOffset);' en el atributo ajax 'success' para que el desplazamiento funcione correctamente – user3284463

+0

¡Impresionante! ¡Gracias, Jeff! – Can

5

No hay necesidad de jQuery aquí, sólo comprobar si hay cambios en los elementos scrollHeight y ajustar el scrollTop consecuencia, es decir:

var lastScrollHeight = el.scrollHeight; 
for(var n=0; n<10; n++){ 
    // Prepend here... 
} 
var scrollDiff = el.scrollHeight - lastScrollHeight; 
el.scrollTop += scrollDiff; 

demostración

http://jsfiddle.net/fkqqv28e/

jQuery

para acceder al nodo DOM nativa de una colección jQuery, utilizar acceso a una matriz; es decir:

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

Acabo de toparme con una variación de esto. Desconecté una gran cantidad de elementos, los procesé y luego los agregué nuevamente. esto no se completa sincrónicamente en Chrome, así que no pude establecer $ el.scrollTop (oldval) de manera confiable. Encontré que esta solución funcionó para mí.

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

Algunas personas que vienen aquí sólo quieren añadir contenido sin el enfoque actual saltando. La modificación de la demostración anterior de Jeff B para usar el destino del evento Click hace que este idioma sea más portátil:

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 demostración anterior de Jeff B pero con el botón [agregar más] en un punto arbitrario de la página).

0

También puede mantener la posición de desplazamiento en función del desplazamiento en la parte inferior de la página/elemento.

var ScrollFromBottom = $(document).height() - $(window).scrollTop(); 

//ajax - add messages -- geenrate html code then add to dom 

//set scroll position 
$(window).scrollTop($(document).height() - ScrollFromBottom); 
Cuestiones relacionadas