jQuery scroll () détecte quand l'utilisateur arrête de faire défiler

109

Ok avec ça ...

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

Je peux dire quand quelqu'un fait défiler ce que je comprends. Donc, avec cela, j'essaie de comprendre comment attraper quand quelqu'un s'est arrêté. Dans l'exemple ci-dessus, vous pouvez voir que je supprime une classe d'un ensemble d'éléments pendant le défilement. Cependant, je souhaite réactiver cette classe lorsque l'utilisateur arrête de défiler.

La raison en est que j'ai l'intention d'avoir une émission en escale pendant que la page défile pour donner à la page un effet spécial sur lequel j'essaie de travailler. Mais la classe que j'essaie de supprimer lors du défilement entre en conflit avec cet effet, car c'est un effet de transparence pour une certaine nature.

Chris
la source
Génial, pas exactement en double mais définitivement dans l'allée de ce que je cherchais et m'a aidé à la fin à résoudre mon problème. Je vous remercie.
chris

Réponses:

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

Mettre à jour

J'ai écrit une extension pour améliorer le on-event-handler par défaut de jQuery . Il attache une fonction de gestionnaire d'événements pour un ou plusieurs événements aux éléments sélectionnés et appelle la fonction de gestionnaire si l'événement n'a pas été déclenché pendant un intervalle donné. Ceci est utile si vous souhaitez déclencher un rappel uniquement après un délai, comme l'événement de redimensionnement, ou autre.

Il est important de vérifier le github-repo pour les mises à jour!

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

Utilisez-le comme n'importe quel autre gestionnaire onou bind-event, sauf que vous pouvez passer un paramètre supplémentaire en dernier:

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

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

(cette démo utilise à la resizeplace de scroll, mais qui s'en soucie?!)

yckart
la source
Ce n'est toujours pas précis à 100%: parfois l'utilisateur s'arrête et reprend le défilement même après 250 ms
Arman Bimatov
Ce code fonctionne très bien, mais il a totalement cassé le widget de saisie semi-automatique de jquery ui.
kkazakov
@ArmanBimatov alors il sera considéré comme l'utilisateur continue à faire défiler, ce qui sonne bien, non?
godblessstrawberry
Ce délai d'expiration se déclenche uniquement lorsque les événements de défilement s'arrêtent et NON lorsque l'utilisateur arrête le défilement. L'utilisateur peut lever le doigt de la souris et le défilement peut se poursuivre pendant quelques secondes en fonction de la vitesse de son défilement. Cette solution ne vous donnera pas d'indication lorsque l'utilisateur a arrêté le défilement.
AndroidDev
1
@abzarak cette aide abstraite n'est pas parfaite, en aucun cas! Je n'ai pas mis à jour le github-repo récemment, pour des raisons - c'était une idée terrible. Utilisez simplement une fonction wrapper "throttle" ou "debounce" à la place. Je dois noter que ailleurs aussi! :)
yckart
49

Utilisation de jQuery throttle / debounce

jQuery debounce est un bon pour des problèmes comme celui-ci. jsFidlle

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

Le deuxième paramètre est le drapeau "at_begin". Ici, j'ai montré comment exécuter du code à la fois au «début du défilement» et à la «fin du défilement».

Utiliser Lodash

Comme suggéré par Barry P, jsFiddle , underscore ou lodash ont également un anti -rebond, chacun avec des apis légèrement différents.

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

$(window).scroll(_.debounce(function(){
    $('#scrollMsg').html('STOPPED!');
}, 150));
Sinetheta
la source
Est-il possible d'utiliser une fonction de défilement normale en même temps? $ (fenêtre) .scroll (fonction () {...});
Daniel Vogelnest
Bien sûr, jQuery liera autant de gestionnaires à un événement que vous le souhaitez.
Sinetheta
Merci d'avoir mis à jour ce @BarryP Jsfiddle fournit également lo-dash afin que vous puissiez éviter le lien externe jsfiddle.net/qjggnyhf
Sinetheta
Pour info, j'avais des problèmes où les parchemins rapides ne revenaient pas. Il semblait que vous deviez ajouter quelques millisecondes au anti-rebond "STOPPED", sinon cela provoque une condition de concurrence où, parfois, STOPPED se déclenche avant le STARTED, et vous vous retrouvez avec l'élément bloqué comme si vous continuiez à faire défiler. J'ai fait le mien 150 et 160, respectivement, et cela semblait faire l'affaire.
CodeChimp
Merci @CodeChimp c'est chouette, mais je m'inquiétais de gérer les cas de bord en les corrigeant 15 fois sur 16;) Peut-être qu'un seul gestionnaire avec toute la logique à l'intérieur serait le plus sûr. Vérifiez le leadinget trailingvous - même, puis assurez-vous qu'il ne peut y avoir aucune confusion.
Sinetheta
9

Rob W m'a suggéré de consulter un autre article ici sur la pile qui était essentiellement un article similaire à mon article d'origine. Quelle lecture à travers cela j'ai trouvé un lien vers un site:

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

Cela a en fait aidé à résoudre mon problème très bien après quelques ajustements pour mes propres besoins, mais dans l'ensemble, cela a aidé à éliminer beaucoup de bouffées de chaleur et m'a sauvé environ 4 heures de le résoudre par moi-même.

Vu que cet article semble avoir du mérite, j'ai pensé que je reviendrais et fournirais le code trouvé à l'origine sur le lien mentionné, juste au cas où l'auteur déciderait d'aller dans une direction différente avec le site et finirait par supprimer le lien.

(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
la source
5

Je suis d'accord avec certains des commentaires ci-dessus que l'écoute pendant un délai d'attente n'était pas assez précise car cela se déclenchera lorsque vous arrêterez de déplacer la barre de défilement assez longtemps au lieu de lorsque vous arrêtez de défiler. Je pense qu'une meilleure solution est d'écouter l'utilisateur qui lâche la souris (mouseup) dès qu'il commence à faire défiler:

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

et un exemple de son fonctionnement peut être vu dans ce JSFiddle

Théo
la source
2
Cela semble génial, mais si vous faites défiler avec un geste à 2 doigts sur un trackpad ou une molette de défilement, la souris n'est pas déclenchée. C'est probablement le moyen le plus courant de faire défiler aussi, ce qui le rend problématique.
Adam
1
Bon point. Mais il existe potentiellement quelques correctifs pour cela. Utiliser l'événement 'mousewheel' de jquery ou suivre si la souris est en premier, et utiliser une approche de temporisation comme suggéré par d'autres. Mais je pense que l'utilisation d'une combinaison d'autres réponses pour les événements de la molette de la souris et cette réponse pour le glissement de la barre de défilement donnera les résultats les plus précis
Theo
3

Vous pouvez définir un intervalle qui s'exécute toutes les 500 ms environ, dans le sens de ce qui suit:

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

Je n'ai pas testé ce code, mais le principe devrait fonctionner.

Justinbach
la source
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);

Version très petite avec capacité de début et de fin

Dieter Bender
la source
1

Ok c'est quelque chose que j'ai déjà utilisé. Fondamentalement, vous regardez une prise de référence jusqu'au dernier scrollTop(). Une fois votre délai écoulé, vous vérifiez le courant scrollTop()et s'ils sont les mêmes, vous avez terminé le défilement.

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

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

  currentScrollTop = $(this).scrollTop()
)
matrangam
la source
1

Le style ES6 avec contrôle du défilement démarre également.

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

Usage:

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
la source
0

Veuillez vérifier l' événement jquery mobile scrollstop

$(document).on("scrollstop",function(){
  alert("Stopped scrolling!");
});
Irshad Pathan
la source
3
c'est jquery mobile, pas jquery. je suis presque tombé dans le même piège;)
katzenhut
0

Pour ceux qui en ont encore besoin, voici la solution

  $(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
la source
0

Cela devrait fonctionner:

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

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

Voici comment vous pouvez gérer cela:

    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
la source
0

Cela détecte l'arrêt de défilement après 1 milliseconde (ou le change) à l'aide d'une minuterie globale:

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
la source