2012-03-08 11 views
42

Tengo una página con "desplazamiento infinito". Calcula la diferencia entre el final de la página y la página actual y carga más contenido si esta diferencia es lo suficientemente pequeña. El código es soemthing como esto usando jQuery:Evento de desplazamiento que se dispara demasiadas veces. Solo quiero disparar un máximo de, digamos, una vez por segundo

$(window).on('scroll', function() { 
    if (window.pageYOffset > loadMoreButton.offsetTop - 1000) 
     # load more content via ajax 
} 

Ahora, el problema es que cada vez que me desplazo, este evento se dispara varias veces por desplazamiento. Me gustaría disparar a lo sumo cada x milisegundos. ¿Cómo haría esto?

Respuesta

37

Consulte el método "acelerador" de la biblioteca Underscore.js.

http://underscorejs.org/#throttle

El ejemplo que da es exactamente lo que estás preguntando - limitar la frecuencia con que tiene que manejar eventos de desplazamiento.

+2

bastante impresionante, pero estoy tratando de evitar cualquier dependencia. –

+14

Mire el código fuente para subrayar (que está increíblemente documentado) y simplemente extraiga el acelerador. – tkone

0

el disparo en espiral varias veces es correcto y debe poder obtener la posición de desplazamiento de forma diferente cada vez. Creo que necesitas establecer un temporizador cuando ingresas por primera vez en el evento de desplazamiento como mencionaste x milisegundos, y también registras la marca de tiempo, y luego la próxima vez que disparas el evento, comprueba la última hora de disparo e ignóralo si está dentro de x milisegundos , y haz el trabajo real en tu acción de Timer.

6
var isWorking = 0; 

$(window).on('scroll', function() 
{ 
    if(isWorking==0) 
    { 
     isWorking=1; 
     if (window.pageYOffset > loadMoreButton.offsetTop - 1000) 
     # load more content via ajax 
     setTimeout(function(){isWorking=0},1000); 
    } 
} 
+0

Espero que funcione, no lo probé. Básicamente, ejecuta cada pergamino pero si se está ejecutando otra copia no hace nada, si no espera xxx milisegundos y hace el trabajo –

+0

Eeeeck. No puede usar una función sleep como esta en javascript. Colgará el navegador mientras duerme. Javascript tiene un solo subproceso y bloquea el navegador. Simplemente no puede escribir código javascript como este. – jfriend00

+0

sí, tiene razón, se olvidó de esto –

30

Una forma de resolver este problema es definir un intervalo de tiempo y solo procesar un evento de desplazamiento una vez dentro de ese intervalo de tiempo. Si aparece más de un evento de desplazamiento durante ese intervalo de tiempo, lo ignorará y lo procesará solo cuando haya transcurrido ese intervalo de tiempo.

var scrollTimer, lastScrollFireTime = 0; 

$(window).on('scroll', function() { 

    var minScrollTime = 100; 
    var now = new Date().getTime(); 

    function processScroll() { 
     console.log(new Date().getTime().toString()); 
    } 

    if (!scrollTimer) { 
     if (now - lastScrollFireTime > (3 * minScrollTime)) { 
      processScroll(); // fire immediately on first scroll 
      lastScrollFireTime = now; 
     } 
     scrollTimer = setTimeout(function() { 
      scrollTimer = null; 
      lastScrollFireTime = new Date().getTime(); 
      processScroll(); 
     }, minScrollTime); 
    } 
}); 

Esto se disparará el primer evento de desplazamiento de inmediato y luego se obtiene un evento de desplazamiento aproximadamente una vez cada 100 ms, mientras que la barra de desplazamiento se mueve y entonces un evento final después de la barra de desplazamiento deja de moverse. Puede ajustar la frecuencia del evento cambiando el argumento al setTimeout (lo que actualmente está configurado a 100).

Hay una demostración aquí: http://jsfiddle.net/jfriend00/EBEqZ/ que necesita para abrir una ventana de consola de depuración, comience a mover la barra de desplazamiento en la ventana de contenido y luego vea la hora de cada evento de desplazamiento en la ventana de la consola de depuración. En mi versión de Chrome, están configurados para un espaciado mínimo de 100 ms y parecen ocurrir cada 100-200 ms.

+0

Hola Jfriend, cuando presiono la tecla "fin" del teclado en mi página infinita, parece que el proceso Desplillo se dispara dos veces. ¿Cómo puedo resolver eso? – horse

+0

@ user2356198 - Aquí hay una versión que resuelve ese problema: http://jsfiddle.net/v8e7aLy6/ – jfriend00

+0

Está bien para la tecla "final", pero cuando me desplazo rápidamente, se dispara dos veces. No hay problema en el desplazamiento lento. – horse

4
var now = new Date().getTime(); 
$(window).scroll(function() { 
    if (window.pageYOffset > loadMoreButton.offsetTop - 1000) 
    { 
     if (new Date().getTime() - now > 1000) 
     { 
      console.log("Task executed once per second"); 
      now = new Date().getTime(); 
     } 
    } 
}); 

O

Puede que utilice el límite fonction llama: throttling-function-calls

function throttle(fn, threshhold, scope) { 
    threshhold || (threshhold = 250); 
    var last, 
     deferTimer; 
    return function() { 
    var context = scope || this; 

    var now = +new Date, 
     args = arguments; 
    if (last && now < last + threshhold) { 
     // hold on to it 
     clearTimeout(deferTimer); 
     deferTimer = setTimeout(function() { 
     last = now; 
     fn.apply(context, args); 
     }, threshhold); 
    } else { 
     last = now; 
     fn.apply(context, args); 
    } 
    }; 
} 

Se le puede llamar así:

$('body').on('mousemove', throttle(function (event) { 
    console.log('tick'); 
}, 1000)); 
+2

Por favor, agregue algunos comentarios y descripción a este código. –

3

Aquí es una solución que doesn no requiere el uso de un extr una biblioteca JS o complemento, que apunta a la simplicidad. Puede que no sea tan eficiente como otras implementaciones, pero definitivamente es un paso adelante de disparar tu evento principal cada vez que te desplazas.

Esto fue tomado de este blog post por Danny Van Kooten. Que he usado para retrasar mis eventos onscroll() para mi botón de volver arriba en mi blog.

var timer; 
$(window).scroll(function() { 
    if(timer) { 
     window.clearTimeout(timer); 
    } 
    timer = window.setTimeout(function() { 
     // actual code here. Your call back function. 
    console.log("Firing!"); 
    }, 100); 
}); 

También puede mejorar aún más el rendimiento moviendo a cabo las variables de la función de devolución de llamada para evitar cálculos innecesarios, por ejemplo el valor de $(window).height() o la altura de algún elemento div estática que no va a cambiar una vez que la página se carga .

Aquí hay un ejemplo que está adaptado de mi caso de uso.

var scrollHeight = $("#main-element").height(); //never changes, no need to recalculate. 
$(window).on('scroll', function() { 
    if (timer) 
     window.clearTimeout(timer); 
    timer = window.setTimeout(function() { 
     var scrollPosition = $(window).height() + $(window).scrollTop();  
     if ($(window).scrollTop() < 500) 
      $(".toggle").fadeIn(800); 
     else 
      $(".toggle").fadeOut(800); 
    }, 150); //only fire every 150 ms. 
}); 

Esto limita la función real que sólo se ejecute cada 150 ms, o de lo contrario restablecer el temporizador de nuevo a 0 si no ha pasado de 150 ms. Ajusta el valor para que se ajuste a lo que necesitas.

9

Hay una buena explicación de John Resig, el creador de jQuery para resolver esta situación.

var outerPane = $details.find(".details-pane-outer"), 
    didScroll = false; 

$(window).scroll(function() { 
    didScroll = true; 
}); 

setInterval(function() { 
    if (didScroll) { 
     didScroll = false; 
     // Check your page position and then 
     // Load in more results 
    } 
}, 250); 

La fuente: Biblioteca http://ejohn.org/blog/learning-from-twitter/

Cuestiones relacionadas