jQuery scroll()检测用户何时停止滚动


109

好的。

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

我可以从我的理解中得知某人正在滚动。因此,我正尝试找出如何在有人停止活动时赶上。从上面的示例中,您可以看到发生滚动时,我正在从一组元素中删除一个类。但是,我想在用户停止滚动时重新打开该类。

这样做的原因是,我打算在页面滚动时进行中转节目,以使页面具有我要尝试的特殊效果。但是我尝试在滚动时删除的一个类与该效果冲突,因为它对某种性质是透明的。



很棒,虽然不完全重复,但是绝对可以在我所寻找的小巷中找到,并最终帮助我解决了问题。谢谢。
克里斯,2012年

Answers:


253
$(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。它将一个或多个事件的事件处理程序函数附加到选定的元素,如果在给定时间间隔内未触发事件,则调用处理程序函数。如果您只想在延迟(例如resize事件等)之后才触发回调,则此功能很有用。

检查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毫秒后仍会停止并恢复滚动
Arman Bimatov 2014年

这段代码很好用,但它完全破坏了jQuery ui的自动完成小部件。
kkazakov

@ArmanBimatov然后它将被视为用户不断滚动,听起来不错,不是吗?
godblessstrawberry

仅在滚动事件停止时触发此超时,而在用户停止滚动时不触发。用户可以从鼠标上抬起手指,根据他们的滚动速度,滚动可以继续几秒钟。当用户停止滚动时,此解决方案将不会为您提供指示。
AndroidDev

1
@abzarak这个抽象助手在任何情况下都不是完美的!由于某种原因,我最近没有更新github-repo-这是一个糟糕的主意。只需使用“ throttle”或“ debounce”包装函数即可。我应该注意其他地方!:)
yckart

49

使用jQuery节流阀/反跳

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

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

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

使用Lodash

正如Barry P所建议的那样,jsFiddle下划线lodash也具有反跳功能,每个API的API略有不同。

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

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

是否可以同时使用普通滚动功能?$(window).scroll(function(){...});
Daniel Vogelnest 2013年

当然,jQuery将根据需要将尽可能多的处理程序绑定到一个事件。
Sinetheta

感谢更新此@BarryP Jsfiddle还提供了破折号,因此您可以避免使用外部链接jsfiddle.net/qjggnyhf
Sinetheta 2014年

仅供参考,我遇到的问题是快速滚动无法还原。似乎您需要为“ STOPPED”防抖添加几毫秒,否则会导致竞争状况,有时STOPPED在STARTED之前触发,最终导致项目被卡住,就像您仍在滚动一样。我分别让我的150和160成为了我的选择,这似乎可以解决问题。
CodeChimp

感谢@CodeChimp的简洁,但是我担心通过在16种情况中将其修复15次来处理边缘情况;)也许其中包含所有逻辑的单个处理程序是最安全的。检查leading和您trailing自己,然后确保没有混淆。
Sinetheta's

9

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

})();

5

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

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

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


2
这似乎很棒,但是如果您在触控板上或滚轮上通过2手指手势滚动,则不会触发mouseup。这可能也是最常见的滚动方式,这使其成为问题。
亚当

1
好点子。但是可能有一些修复方法。使用jquery的'mousewheel'事件或跟踪是否首先按下鼠标,并使用其他人建议的超时方法。但我认为,结合使用其他其他答案来解决鼠标滚轮事件以及此问题来解决滚动条拖动问题,将得出最准确的结果
Theo

3

您可以按照以下方式设置每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);

我没有测试过此代码,但是原理应该可行。


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

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


1

好的,这是我以前使用过的东西。基本上,您会保留对最后一个的引用scrollTop()。清除超时后,检查当前值scrollTop(),如果它们相同,则滚动完成。

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

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

  currentScrollTop = $(this).scrollTop()
)

1

具有检查滚动开始的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
}))


0

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

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



0

这是您可以如何处理的方法:

    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>


0

这将使用全局计时器在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.
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.