jQuery scroll () detecta cuando el usuario deja de desplazarse

109

Ok con esto ...

$(window).scroll(function()
{
    $('.slides_layover').removeClass('showing_layover');
    $('#slides_effect').show();
});

Puedo saber cuándo alguien se desplaza por lo que entiendo. Entonces, con eso estoy tratando de descubrir cómo detectar cuando alguien se ha detenido. En el ejemplo anterior, puede ver que estoy eliminando una clase de un conjunto de elementos mientras se produce el desplazamiento. Sin embargo, quiero volver a activar esa clase cuando el usuario deje de desplazarse.

La razón de esto es que tengo la intención de mostrar una escala mientras la página se desplaza para darle a la página un efecto especial en el que estoy tratando de trabajar. Pero la única clase que estoy tratando de eliminar mientras me desplazo entra en conflicto con ese efecto, ya que es un efecto de transparencia para alguna naturaleza.

chris
fuente
1
posible duplicado del evento
Rob W
Impresionante, no exactamente duplicado, pero definitivamente en el callejón de lo que estaba buscando y me ayudó al final a resolver mi problema. Gracias.
chris

Respuestas:

253
$(window).scroll(function() {
    clearTimeout($.data(this, 'scrollTimer'));
    $.data(this, 'scrollTimer', setTimeout(function() {
        // do something
        console.log("Haven't scrolled in 250ms!");
    }, 250));
});

Actualizar

Escribí una extensión para mejorar el oncontrolador de eventos predeterminado de jQuery . Adjunta una función de controlador de eventos para uno o más eventos a los elementos seleccionados y llama a la función de controlador si el evento no se activó durante un intervalo determinado. Esto es útil si desea disparar una devolución de llamada solo después de un retraso, como el evento de cambio de tamaño o similar.

¡Es importante comprobar si hay actualizaciones en el repositorio de github!

https://github.com/yckart/jquery.unevent.js

;(function ($) {
    var on = $.fn.on, timer;
    $.fn.on = function () {
        var args = Array.apply(null, arguments);
        var last = args[args.length - 1];

        if (isNaN(last) || (last === 1 && args.pop())) return on.apply(this, args);

        var delay = args.pop();
        var fn = args.pop();

        args.push(function () {
            var self = this, params = arguments;
            clearTimeout(timer);
            timer = setTimeout(function () {
                fn.apply(self, params);
            }, delay);
        });

        return on.apply(this, args);
    };
}(this.jQuery || this.Zepto));

Úselo como cualquier otro controlador de eventos ono bind, excepto que puede pasar un parámetro adicional como último:

$(window).on('scroll', function(e) {
    console.log(e.type + '-event was 250ms not triggered');
}, 250);

http://yckart.github.com/jquery.unevent.js/

(esta demostración usa en resizelugar de scroll, ¡pero a quién le importa!)

yckart
fuente
Todavía no es 100% exacto: a veces el usuario se detiene y reanuda el desplazamiento incluso después de 250 ms
Arman Bimatov
Este código funciona muy bien, pero rompió totalmente el widget de autocompletar de jquery ui.
kkazakov
@ArmanBimatov, entonces se considerará que el usuario sigue desplazándose, lo que suena bien, ¿no?
godblessstrawberry
Este tiempo de espera solo se activa cuando los eventos de desplazamiento se detienen y NO cuando el usuario deja de desplazarse. El usuario puede levantar el dedo del mouse y el desplazamiento puede continuar durante unos segundos dependiendo de la velocidad de su desplazamiento. Esta solución no le dará una indicación cuando el usuario haya dejado de desplazarse.
AndroidDev
1
@abzarak ¡este ayudante abstracto no es perfecto, en ningún caso! No he actualizado el github-repo recientemente, por razones: esta fue una idea terrible. En su lugar, utilice una función de envoltura "acelerador" o "antirrebote". ¡Debo señalar eso en otro lugar también! :)
yckart
49

Usando jQuery throttle / debounce

jQuery debounce es bueno para problemas como este. jsFidlle

$(window).scroll($.debounce( 250, true, function(){
    $('#scrollMsg').html('SCROLLING!');
}));
$(window).scroll($.debounce( 250, function(){
    $('#scrollMsg').html('DONE!');
}));

El segundo parámetro es la bandera "at_begin". Aquí he mostrado cómo ejecutar código tanto en el "inicio del desplazamiento" como en el "final del desplazamiento".

Usando Lodash

Como sugirió Barry P, jsFiddle , subrayado o lodash también tienen un rebote, cada uno con apis ligeramente diferentes.

$(window).scroll(_.debounce(function(){
    $('#scrollMsg').html('SCROLLING!');
}, 150, { 'leading': true, 'trailing': false }));

$(window).scroll(_.debounce(function(){
    $('#scrollMsg').html('STOPPED!');
}, 150));
Sinetheta
fuente
¿Es posible utilizar una función de desplazamiento normal al mismo tiempo? $ (ventana) .scroll (function () {...});
Daniel Vogelnest
Por supuesto, jQuery vinculará tantos controladores a un evento como desee.
Sinetheta
Gracias por actualizar este @BarryP Jsfiddle también proporciona lo-dash para que pueda evitar el enlace externo jsfiddle.net/qjggnyhf
Sinetheta
Para su información, estaba teniendo problemas en los que los pergaminos rápidos no se revertían. Parecía que necesitabas agregar algunos milisegundos al antirrebote "DETENIDO", de lo contrario, se produce una condición de carrera en la que, en algún momento, el DETENIDO se activa antes que el INICIADO y terminas con el elemento atascado como si aún estuvieras desplazándote. Hice el mío 150 y 160, respectivamente, y pareció funcionar.
CodeChimp
Gracias @CodeChimp, eso es genial, pero me preocupaba manejar casos extremos arreglándolos 15 de 16 veces;) Tal vez un solo controlador con toda la lógica adentro sería más seguro. Compruebe el leadingy trailingusted mismo, luego asegúrese de que no pueda haber confusión.
Sinetheta
9

Rob W sugirió que revisara otra publicación aquí en la pila que era esencialmente una publicación similar a la original. Al leer eso encontré un enlace a un sitio:

http://james.padolsey.com/javascript/special-scroll-events-for-jquery/

En realidad, esto terminó ayudando a resolver mi problema muy bien después de algunos ajustes para mis propias necesidades, pero sobre todo me ayudó a eliminar muchas de las tonterías y me ahorró alrededor de 4 horas de resolverlo por mi cuenta.

Dado que esta publicación parece tener algún mérito, pensé que volvería y proporcionaría el código que se encuentra originalmente en el enlace mencionado, en caso de que el autor alguna vez decidiera ir en una dirección diferente con el sitio y terminara eliminando el enlace.

(function(){

    var special = jQuery.event.special,
        uid1 = 'D' + (+new Date()),
        uid2 = 'D' + (+new Date() + 1);

    special.scrollstart = {
        setup: function() {

            var timer,
                handler =  function(evt) {

                    var _self = this,
                        _args = arguments;

                    if (timer) {
                        clearTimeout(timer);
                    } else {
                        evt.type = 'scrollstart';
                        jQuery.event.handle.apply(_self, _args);
                    }

                    timer = setTimeout( function(){
                        timer = null;
                    }, special.scrollstop.latency);

                };

            jQuery(this).bind('scroll', handler).data(uid1, handler);

        },
        teardown: function(){
            jQuery(this).unbind( 'scroll', jQuery(this).data(uid1) );
        }
    };

    special.scrollstop = {
        latency: 300,
        setup: function() {

            var timer,
                    handler = function(evt) {

                    var _self = this,
                        _args = arguments;

                    if (timer) {
                        clearTimeout(timer);
                    }

                    timer = setTimeout( function(){

                        timer = null;
                        evt.type = 'scrollstop';
                        jQuery.event.handle.apply(_self, _args);

                    }, special.scrollstop.latency);

                };

            jQuery(this).bind('scroll', handler).data(uid2, handler);

        },
        teardown: function() {
            jQuery(this).unbind( 'scroll', jQuery(this).data(uid2) );
        }
    };

})();
chris
fuente
5

Estuve de acuerdo con algunos de los comentarios anteriores en que escuchar un tiempo de espera no era lo suficientemente preciso, ya que se activará cuando dejes de mover la barra de desplazamiento durante el tiempo suficiente en lugar de cuando dejes de desplazarte. Creo que una mejor solución es escuchar al usuario que suelta el mouse (mouseup) tan pronto como comienza a desplazarse:

$(window).scroll(function(){
    $('#scrollMsg').html('SCROLLING!');
    var stopListener = $(window).mouseup(function(){ // listen to mouse up
        $('#scrollMsg').html('STOPPED SCROLLING!');
        stopListner(); // Stop listening to mouse up after heard for the first time 
    });
});

y un ejemplo de cómo funciona se puede ver en este JSFiddle

Theo
fuente
2
Esto parece genial, pero si se desplaza con un gesto de 2 dedos en un trackpad o una rueda de desplazamiento, el mouseup no se activa. Esta es probablemente la forma más común de desplazarse también, lo que la hace problemática.
Adam
1
Buen punto. Pero potencialmente hay un par de soluciones para eso. Usar el evento 'mousewheel' de jquery o realizar un seguimiento de si se pulsa primero el mouse y usar un enfoque de tiempo de espera como lo sugirieron otros. Pero creo que usar una combinación de otras respuestas para los eventos de la rueda del mouse y esta respuesta para arrastrar la barra de desplazamiento dará los resultados más precisos
Theo
3

Puede establecer un intervalo que se ejecute cada 500 ms aproximadamente, siguiendo las líneas de lo siguiente:

var curOffset, oldOffset;
oldOffset = $(window).scrollTop();
var $el = $('.slides_layover'); // cache jquery ref
setInterval(function() {
  curOffset = $(window).scrollTop();
  if(curOffset != oldOffset) {
    // they're scrolling, remove your class here if it exists
    if($el.hasClass('showing_layover')) $el.removeClass('showing_layover');
  } else {
    // they've stopped, add the class if it doesn't exist
    if(!$el.hasClass('showing_layover')) $el.addClass('showing_layover');
  }
  oldOffset = curOffset;
}, 500);

No he probado este código, pero el principio debería funcionar.

Justinbach
fuente
2
function scrolled() {
    //do by scroll start
    $(this).off('scroll')[0].setTimeout(function(){
        //do by scroll end
        $(this).on('scroll',scrolled);
    }, 500)
}
$(window).on('scroll',scrolled);

Versión muy pequeña con capacidad de inicio y fin

Dieter Bender
fuente
1

Ok, esto es algo que he usado antes. Básicamente buscas mantener una referencia a la última scrollTop(). Una vez que se borra el tiempo de espera, verifica la corriente scrollTop()y, si son iguales, termina el desplazamiento.

$(window).scroll((e) ->
  clearTimeout(scrollTimer)
  $('header').addClass('hidden')

  scrollTimer = setTimeout((() ->
    if $(this).scrollTop() is currentScrollTop
      $('header').removeClass('hidden') 
  ), animationDuration)

  currentScrollTop = $(this).scrollTop()
)
matrangam
fuente
1

Estilo ES6 con control de inicio de desplazamiento también.

function onScrollHandler(params: {
  onStart: () => void,
  onStop: () => void,
  timeout: number
}) {
  const {onStart, onStop, timeout = 200} = params
  let timer = null

  return (event) => {
    if (timer) {
      clearTimeout(timer)
    } else {
      onStart && onStart(event)
    }
    timer = setTimeout(() => {
      timer = null
      onStop && onStop(event)
    }, timeout)
  }
}

Uso:

yourScrollableElement.addEventListener('scroll', onScrollHandler({
  onStart: (event) => {
    console.log('Scrolling has started')
  },
  onStop: (event) => {
    console.log('Scrolling has stopped')
  },
  timeout: 123 // Remove to use default value
}))
Ilya Iksent
fuente
0

por favor revise el evento jquery mobile scrollstop

$(document).on("scrollstop",function(){
  alert("Stopped scrolling!");
});
Irshad Pathan
fuente
3
eso es jquery mobile, no jquery. Casi
caigo
0

Para aquellos que todavía necesitan esto, aquí está la solución

  $(function(){
      var t;
      document.addEventListener('scroll',function(e){
          clearTimeout(t);
          checkScroll();
      });
      
      function checkScroll(){
          t = setTimeout(function(){
             alert('Done Scrolling');
          },500); /* You can increase or reduse timer */
      }
  });

smacaz
fuente
0

Esto debería funcionar:

var Timer;
$('.Scroll_Table_Div').on("scroll",function() 
{
    // do somethings

    clearTimeout(Timer);
    Timer = setTimeout(function()
    {
        console.log('scrolling is stop');
    },50);
});
Mohamad Hamouday
fuente
0

Así es como puede manejar esto:

    var scrollStop = function (callback) {
        if (!callback || typeof callback !== 'function') return;
        var isScrolling;
        window.addEventListener('scroll', function (event) {
            window.clearTimeout(isScrolling);
            isScrolling = setTimeout(function() {
                callback();
            }, 66);
        }, false);
    };
    scrollStop(function () {
        console.log('Scrolling has stopped.');
    });
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>.<br>
</body>
</html>

Mohammad Ghonchesefidi
fuente
0

Esto detecta la parada de desplazamiento después de 1 milisegundo (o lo cambia) usando un temporizador global:

var scrollTimer;

$(window).on("scroll",function(){
    clearTimeout(scrollTimer);
    //Do  what you want whilst scrolling
    scrollTimer=setTimeout(function(){afterScroll()},1);
})

function afterScroll(){
    //I catched scroll stop.
}
Ali Sheikhpour
fuente