javascript pagina Scroll evento disparando demasiadas veces. Solo quiero disparar un máximo de, digamos, una vez por segundo



onload jquery (7)

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

http://underscorejs.org/#throttle

El ejemplo que proporciona es exactamente lo que está preguntando, lo que limita la frecuencia con la que debe manejar los eventos de desplazamiento.

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 parecido a esto usando jQuery:

$(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 dispara varias veces por desplazamiento. Me gustaría disparar a lo sumo cada x milisegundos. ¿Cómo haría esto?


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 utilizar llamadas de función de throttling-function-calls : llamadas de función de 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);
    }
  };
}

Puedes llamarlo así:

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

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: http://ejohn.org/blog/learning-from-twitter/


No necesita un gran segmento de código para una función de aceleración decente. El propósito de una función del acelerador es reducir los recursos del navegador, no aplicar tanta sobrecarga que está utilizando aún más. Además, mis diferentes usos para las funciones del acelerador requieren muchas circunstancias diferentes para ellos. Aquí está mi lista de cosas que una 'buena' función de acelerador necesita que esta tenga.

  • Mínimo sobrecarga
  • Llamada de función inmediata si ha sido más que intervalo MS desde la última llamada.
  • Evitar la ejecución de la función para otro intervalo MS.
  • Retrasando la activación excesiva de eventos en lugar de descartar el evento por completo.
  • Actualiza el evento retrasado cuando sea necesario para que no se convierta en "obsoleto".
  • Impide la acción predeterminada del evento cuando la función de aceleración se retrasa.
  • Ser capaz de eliminar el oyente de escucha de eventos de aceleración.

Y, creo que la siguiente función del acelerador satisface todos esos.

var cachedThrottleFuncs = [],
    minimumInterval = 200; // minimum interval between throttled function calls
function throttle(func, obj, evt) {
    var timeouttype = 0,
        curFunc;
    function lowerTimeoutType(f){
        timeouttype=0;
        if (curFunc !== undefined){
            curFunc();
            curFunc = undefined;
        }
    };
    return cachedThrottleFuncs[ ~(
        ~cachedThrottleFuncs.indexOf(func) || 
        ~(
          cachedThrottleFuncs.push(function(Evt) {
            switch (timeouttype){
                case 0: // Execute immediatly
                    ++timeouttype;
                    func.call(Evt.target, Evt);
                    setTimeout(lowerTimeoutType, minimumInterval);
                    break;
                case 1: // Delayed execute
                    curFunc = func.bind(Evt.target, Evt);
                    Evt.preventDefault();
            }
          }) - 1
        )
    )];
};
function listen(obj, evt, func){
    obj.addEventListener(evt, throttle(func, obj, evt));
};
function mute(obj, evt, func){
    obj.removeEventListener(evt, throttle(func, obj, evt));
}

Ejemplo de uso:

listen(document.body, 'scroll', function whenbodyscrolls(){
    if (document.body.scrollTop > 400)
        mute(document.body, 'scroll', whenbodyscrolls();
    else
        console.log('Body scrolled!')
});

Alternativamente, si solo necesita agregar detectores de eventos y no necesita eliminar los detectores de eventos, puede usar la siguiente versión aún más simple.

var minimumInterval = 200; // minimum interval between throttled function calls
function throttle(func, obj, evt) {
    var timeouttype = 0,
        curEvt = null;
    function lowerTimeoutType(f){
        timeouttype=0;
        if (curEvt !== null){
            func(curEvt);
            curEvt = null;
        }
    };
    return function(Evt) {
        switch (timeouttype){
            case 0: // Execute immediately
                ++timeouttype; // increase the timeouttype
                func(Evt);
                // Now, make it so that the timeouttype resets later
                setTimeout(lowerTimeoutType, minimumInterval);
                break;
            case 1: // Delayed execute
                // make it so that when timeouttype expires, your function
                // is called with the freshest event
                curEvt = Evt;
                Evt.preventDefault();
        }
    };
};

Por defecto, esto acelera la función a, como máximo, una llamada cada 200 ms. Para cambiar el intervalo a una cantidad diferente de milisegundos, simplemente cambie el valor de minimumInterval .


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 activará el primer evento de desplazamiento inmediatamente y luego obtendrá un evento de desplazamiento aproximadamente una vez cada 100 ms mientras se mueve la barra de desplazamiento y luego un evento final después de que la barra de desplazamiento deje de moverse. Puede ajustar la frecuencia del evento cambiando el argumento a 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 observe 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.


el disparo de desplazamiento múltiples veces es correcto y debe poder obtener la posición de desplazamiento de manera 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.


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




javascript