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

IT技术 javascript jquery scroll jquery-events
2021-01-30 14:39:22

好的。。

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

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

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

6个回答
$(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,但谁在乎?!)

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

使用 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 所建议的那样,jsFiddle下划线lodash也有去抖动,每个都有略微不同的 api。

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

$(window).scroll(_.debounce(function(){
    $('#scrollMsg').html('STOPPED!');
}, 150));
是否可以同时使用普通的滚动功能?$(window).scroll(function(){ ... });
2021-03-14 14:39:22
感谢您更新此@BarryP Jsfiddle 还提供了 lo-dash,因此您可以避免外部链接jsfiddle.net/qjggnyhf
2021-03-27 14:39:22
仅供参考,我遇到了快速滚动无法恢复的问题。似乎您需要为“STOPPED”去抖动增加几毫秒,否则它会导致竞争条件,有时,STOPPED 在 STARTED 之前触发,最终项目卡住,就好像您仍在滚动一样。我分别做了 150 和 160,它似乎成功了。
2021-03-28 14:39:22
当然,jQuery 会根据您的需要将尽可能多的处理程序绑定到一个事件。
2021-03-30 14:39:22
谢谢@CodeChimp,这很好,但我担心通过修复它们 16 次中的 15 次来处理边缘情况;) 也许一个包含所有逻辑的处理程序是最安全的。检查leadingtrailing你自己,然后确保不会有任何混淆。
2021-03-30 14:39:22

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

})();

我同意上面的一些评论,即监听超时不够准确,因为当您停止移动滚动条足够长的时间而不是停止滚动时会触发超时。我认为更好的解决方案是在用户开始滚动时立即听用户放开鼠标(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 
    });
});

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

这看起来很棒,但是如果您在触控板上通过 2 指手势或滚轮滚动,则不会触发 mouseup。这也可能是最常见的滚动方式,这使得它有问题。
2021-04-03 14:39:22
好点子。但可能有一些修复方法。使用 jquery 的 'mousewheel' 事件或跟踪是否首先鼠标按下,并使用其他人建议的超时方法。但我认为结合使用鼠标滚轮事件的其他答案和滚动条拖动的这个答案将给出最准确的结果
2021-04-05 14:39:22

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