用户停止滚动时的事件

62

当用户滚动页面时,我想使用一些花哨的jQuery效果。但我不知道该如何解决这个问题,因为只有 scroll() 方法。

有什么想法吗?

9个回答

76

您可以使scroll()函数具有超时功能,该功能在每次用户滚动时被覆盖。这样,当用户在一定毫秒数内停止滚动时,您的脚本会运行,但是如果在此期间他再次滚动,则计数器将重新开始并且脚本将等到他再次停止滚动。

更新:

Because this question got some action again I figured I might as well update it with a jQuery extension that adds a scrollEnd event

// extension:
$.fn.scrollEnd = function(callback, timeout) {          
  $(this).on('scroll', function(){
    var $this = $(this);
    if ($this.data('scrollTimeout')) {
      clearTimeout($this.data('scrollTimeout'));
    }
    $this.data('scrollTimeout', setTimeout(callback,timeout));
  });
};

// how to call it (with a 1000ms timeout):
$(window).scrollEnd(function(){
    alert('stopped scrolling');
}, 1000);
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>

<div style="height: 200vh">
  Long div
</div>


6
1000毫秒是很长的时间,最好控制在250左右。 - WantToDo
5
这项技术也被称为事件去抖动。 - Nadeem Ahmad
它甚至在100毫秒内也能很好地工作。不要使用10毫秒,否则滚动会卡住。顺便说一句,你也可以将scrollEnd扩展绑定到DIV上。这里帮了我:https://dev59.com/dXANtIcB2Jgan1zn1Nz3 - Avatar
我该如何稍后停止窗口事件监听器 scrollEnd - user7607751

64

这里是一个简单的例子,使用setTimeout函数在用户停止滚动时触发一个函数:

(function() {        
    var timer;
    $(window).bind('scroll',function () {
        clearTimeout(timer);
        timer = setTimeout( refresh , 150 );
    });
    var refresh = function () { 
        // do stuff
        console.log('Stopped Scrolling'); 
    };
})();

计时器在滚动事件触发时被清除。一旦滚动停止,刷新函数就会触发。
或者作为插件:
$.fn.afterwards = function (event, callback, timeout) {
    var self = $(this), delay = timeout || 16;

    self.each(function () { 
        var $t = $(this);
        $t.on(event, function(){
            if ($t.data(event+'-timeout')) {
                clearTimeout($t.data(event+'-timeout'));
            }
            $t.data(event + '-timeout', setTimeout(function () { callback.apply($t); },delay));
        })
    });
    return this;
};

在具有名称空间的div上,当最后一次滚动事件发生100毫秒后触发回调:
$('div.mydiv').afterwards('scroll.mynamespace', function(e) {
        // do stuff when stops scrolling
        $(this).addClass('stopped');
    }, 100
);

我用这个来处理滚动和调整大小。


3
这被称为“防抖”方法。更多信息(包括可重复使用的函数)在此处:http://davidwalsh.name/function-debounce - allicarn

13
这里有另一个基于相同思路的更通用的解决方案:
var delayedExec = function(after, fn) {
    var timer;
    return function() {
        timer && clearTimeout(timer);
        timer = setTimeout(fn, after);
    };
};

var scrollStopper = delayedExec(500, function() {
    console.log('stopped it');
});

document.getElementById('box').addEventListener('scroll', scrollStopper);

此外,您可以通过将值500更改为较低的值(例如100)来控制触发此事件的速度。 - DHRUV GAJWA

3

我需要实现讨论过的onScrollEnd事件。使用计时器的方法对我很有用。

我使用JavaScript模块模式实现了这个功能:

var WindowCustomEventsModule = (function(){

    var _scrollEndTimeout = 30;

    var _delayedExec = function(callback){
        var timer;
        return function(){
            timer && clearTimeout(timer);
            timer = setTimeout(callback, _scrollEndTimeout);
        }
    };

    var onScrollEnd = function(callback) { 
        window.addEventListener('scroll', _delayedExec(callback), false);         
    };

    return {
        onScrollEnd: onScrollEnd
    }
})();

// usage example
WindowCustomEventsModule.onScrollEnd(function(){
    //
    // do stuff
    //
});

希望这能帮助/激励到某些人。

2
为什么这么复杂?正如文档所指出的那样,这个http://jsfiddle.net/x3s7F/9/是有效的!
$('.frame').scroll(function() {
 $('.back').hide().fadeIn(100);
}

http://api.jquery.com/scroll/.


注意:在Windows Chrome浏览器上,scroll事件与其他所有浏览器不同。您需要快速滚动才能获得与例如FF相同的结果。请查看https://liebdich.biz/back.min.js中的“X”函数。

我的滚动事件需要多少毫秒测试发现如下:

  • Safari、Mac FF、Mac Chrome:每个事件约16ms。
  • Windows FF:每个事件约19ms。
  • Windows Chrome:当缓慢滚动时,每个事件高达约130ms。
  • Internet Explorer:每个事件高达约110ms。

http://jsfiddle.net/TRNCFRMCN/1Lygop32/4/.


实际上,这个很好用。不幸的是,在演示中使用滚动条无法正常工作,尽管我相信这只是由于fadeIn函数的原因。将不得不进行更多测试以查找是否还有其他错误,但做得很好,非常好用!其他解决方案对于这样一个小任务来说都太复杂了。 - Fizzix
谢谢。对于不加注释的点踩者:“现在好一些了吗?”%)P - loveNoHate
现在,如果我只想为向上滚动事件保留条件,那么如何检测向上滚动?你能帮忙吗? - Vijaysinh Parmar

1

并没有名为“scrollEnd”的事件。我建议您使用setInterval每隔一段时间(比如200毫秒)检查scroll()返回的值,并记录当前值与上一个值之间的差值。如果差值变为零,您可以将其用作事件。


1
除非您保留处理程序的引用并在 Delta 变为零时调用 clearInterval,或者只需使用 setTimeout。 - dpq
1
意外地给这个答案投了反对票,现在它被“锁定”了...对此很抱歉。 - blackmiaool

1

1
有scrollstart和scrollstop函数是jquery mobile的一部分。
使用scrollstop的示例:
$(document).on("scrollstop",function(){
   alert("Stopped scrolling!");
});

希望这能帮助到某个人。

这似乎对我没有触发 :( - user736893
@RatherNotsay,这对你不起作用吗?我在生产环境中使用它,看起来运行得很好。你是否包含了jQuery的移动版本?它与jQuery不同。 - Dima
我肯定有使用JQuery Mobile,但可能是缺少了那个组件的自定义版本?目前我已经转而使用其他工具了,但如果我重新使用该工具,我会进行更新。 - user736893

0

我从一个快速拼凑的代码中提取了一些示例,用于演示此功能(请注意,scroll.chain是一个包含两个数组start和end的对象,它们是回调函数的容器)。还请注意,我在这里使用了jQuery和underscore。

$('body').on('scroll', scrollCall);
scrollBind('end', callbackFunction);
scrollBind('start', callbackFunction);

var scrollCall = function(e) {
    if (scroll.last === false || (Date.now() - scroll.last) <= 500) {
        scroll.last = Date.now();
        if (scroll.timeout !== false) {
            window.clearTimeout(scroll.timeout);
        } else {
            _(scroll.chain.start).each(function(f){
                f.call(window, {type: 'start'}, e.event);
            });
        }
        scroll.timeout = window.setTimeout(self.scrollCall, 550, {callback: true, event: e});
        return;
    }
    if (e.callback !== undefined) {
        _(scroll.chain.end).each(function(f){
            f.call(window, {type: 'end'}, e.event);
        });
        scroll.last = false;
        scroll.timeout = false;
    }
};

var scrollBind = function(type, func) {
    type = type.toLowerCase();
    if (_(scroll.chain).has(type)) {
        if (_(scroll.chain[type]).indexOf(func) === -1) {
            scroll.chain[type].push(func);
            return true;
        }
        return false;
    }
    return false;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接