【问题标题】:jQuery scroll() detect when user stops scrollingjQuery scroll() 检测用户何时停止滚动
【发布时间】:2012-02-27 00:14:24
【问题描述】:

没关系。。

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

我可以根据我的理解判断某人何时滚动。因此,我试图弄清楚当有人停下来时如何捕捉。从上面的示例中,您可以看到在滚动发生时我正在从一组元素中删除一个类。但是,我想在用户停止滚动时重新启用该类。

这样做的原因是我打算在页面滚动时进行停留显示,以便为页面提供我正在尝试处理的特殊效果。但是我在滚动时尝试删除的一个类与该效果冲突,因为它对某些性质具有透明效果。

【问题讨论】:

标签: javascript jquery scroll jquery-events


【解决方案1】:
$(window).scroll(function() {
    clearTimeout($.data(this, 'scrollTimer'));
    $.data(this, 'scrollTimer', setTimeout(function() {
        // do something
        console.log("Haven't scrolled in 250ms!");
    }, 250));
});

更新

我写了一个扩展来增强jQuery的默认on-event-handler。它将一个或多个事件的事件处理函数附加到选定元素,如果在给定时间间隔内未触发事件,则调用处理函数。如果您只想在延迟后触发回调,例如调整大小事件等,这将非常有用。

检查 github-repo 的更新很重要!

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

像任何其他 onbind-event 处理程序一样使用它,除了你可以传递一个额外的参数作为最后一个:

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

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

(此演示使用resize 而不是scroll,但谁在乎?!)

【讨论】:

  • 仍然不是 100% 准确:有时用户会在 250 毫秒后停止并继续滚动
  • 这段代码效果很好,但它完全破坏了 jquery ui 的自动完成小部件。
  • @ArmanBimatov 那么它将被视为用户不断滚动,这听起来不错,不是吗?
  • 此超时仅在滚动事件停止时触发,而不是在用户停止滚动时触发。用户可以将手指从鼠标上移开,滚动可以持续几秒钟,具体取决于他们的滚动速度。当用户停止滚动时,此解决方案不会为您提供指示。
  • @abzarak 这个抽象助手并不完美,无论如何!由于某些原因,我最近没有更新 github-repo——这是一个糟糕的主意。只需使用“节流”或“去抖动”包装函数即可。我也应该注意其他地方! :)
【解决方案2】:

使用 jQuery 油门/去抖动

jQuery debounce 是解决此类问题的好方法。 jsFidlle

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

第二个参数是“at_begin”标志。这里我展示了如何在“滚动开始”和“滚动结束”处执行代码。

使用 Lodash

正如 Barry P 所建议的那样,jsFiddleunderscorelodash 也有一个去抖动功能,每个 API 都略有不同。

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

$(window).scroll(_.debounce(function(){
    $('#scrollMsg').html('STOPPED!');
}, 150));

【讨论】:

  • 可以同时使用普通的滚动功能吗? $(window).scroll(function(){ ... });
  • 当然,jQuery 会绑定任意数量的处理程序到一个事件。
  • 感谢更新此@BarryP Jsfiddle 还提供 lo-dash,因此您可以避免外部链接 jsfiddle.net/qjggnyhf
  • 仅供参考,我遇到了快速滚动无法恢复的问题。似乎您需要在“STOPPED”去抖动中增加几毫秒,否则它会导致竞争条件,有时,STOPPED 在 STARTED 之前触发,并且您最终会卡住该项目,就好像您仍在滚动一样。我分别制作了 150 和 160,这似乎可以解决问题。
  • 感谢@CodeChimp,这很整洁,但我担心通过修复 16 次中的 15 次来处理边缘情况;)也许内部包含所有逻辑的单个处理程序是最安全的。自己检查leadingtrailing,然后确保不能混淆。
【解决方案3】:

Rob W 建议我查看堆栈上的另一篇文章,该文章与我原来的文章基本相似。通过阅读我找到了一个网站的链接:

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

在针对我自己的需求进行了一些调整之后,这实际上最终帮助我很好地解决了我的问题,但总的来说,这有助于消除很多麻烦,并为我节省了大约 4 个小时的时间自行解决。

看到这篇文章似乎有一些优点,我想我会回来并提供最初在提到的链接上找到的代码,以防万一作者决定与该网站走不同的方向并最终删除链接。

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

})();

【讨论】:

    【解决方案4】:

    我同意上面的一些 cmets 的意见,即监听超时不够准确,因为当您停止移动滚动条足够长的时间而不是停止滚动时会触发超时。我认为一个更好的解决方案是在用户开始滚动时立即监听用户松开鼠标(mouseup):

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

    this JSFiddle 中可以看到它的工作示例

    【讨论】:

    • 这看起来很棒,但是如果您在触控板上或滚轮上通过两指手势滚动,则不会触发 mouseup。这可能也是最常见的滚动方式,因此会产生问题。
    • 好点。但可能有一些修复方法。使用 jquery 的 'mousewheel' 事件或跟踪是否先按下鼠标,并使用其他人建议的超时方法。但我认为结合使用鼠标滚轮事件的其他答案和滚动条拖动的答案将给出最准确的结果
    【解决方案5】:

    ES6 样式,也检查滚动开始。

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

    用法:

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

    【讨论】:

      【解决方案6】:

      您可以设置一个每隔 500 毫秒左右运行的时间间隔,如下所示:

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

      这段代码我没有测试过,但是原理应该可以的。

      【讨论】:

        【解决方案7】:
        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);
        

        具有开始和结束能力的非常小的版本

        【讨论】:

          【解决方案8】:

          这会使用全局计时器在 1 毫秒(或更改它)后检测滚动停止:

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

          【讨论】:

            【解决方案9】:

            好的,这是我以前用过的东西。 基本上你看起来对最后一个scrollTop() 有一个参考。 一旦你的超时清除,你检查当前的scrollTop(),如果它们相同,你就完成了滚动。

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

            【讨论】:

              【解决方案10】:

              请查看jquery mobile scrollstop event

              $(document).on("scrollstop",function(){
                alert("Stopped scrolling!");
              });
              

              【讨论】:

              • 那是 jquery mobile,不是 jquery。我差点落入同一个陷阱;)
              【解决方案11】:

              对于那些仍然需要这个的人,这里是解决方案

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

              【讨论】:

                【解决方案12】:

                这应该可行:

                var Timer;
                $('.Scroll_Table_Div').on("scroll",function() 
                {
                    // do somethings
                
                    clearTimeout(Timer);
                    Timer = setTimeout(function()
                    {
                        console.log('scrolling is stop');
                    },50);
                });
                

                【讨论】:

                  【解决方案13】:

                  你可以这样处理:

                      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>

                  【讨论】:

                    猜你喜欢
                    • 2011-03-31
                    • 2012-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多