如何使用jQuery / javascript限制事件处理每X秒一次?

8
对于快速触发的 keypress 事件,我希望将事件处理限制在每 X 秒最多一次。
我已经在使用 jQuery 进行事件处理,因此更喜欢基于 jQuery 的解决方案,但纯 JavaScript 也可以。
以下是两个 jsfiddle 链接: - 这个链接 显示了没有任何处理限制的 keypress 快速触发。 - 这个链接 使用 setTimeout() 在 vanilla JS 中实现了每 0.5 秒最多一次的处理限制。
我的问题是:
  1. jQuery有内置的方法来做这个吗?我在 .on() 文档 中没有看到任何内容。

  2. 如果没有,那么在纯JS中做这个有比我在第二个jsfiddle示例中使用的更好的模式吗?


你可能想考虑使用keyup,除非你有一些需要重复的原因。 - SaganRitual
8
链接1:https://code.google.com/p/jquery-debounce/ 链接2:http://www.hnldesign.nl/blog/debouncing-events-with-jquery/这两个链接都是关于使用jQuery来防抖动事件处理的文章。 - zerkms
@zerkms 谢谢,我真的很想在“纯”jQuery中完成这个而不使用插件,但这非常有帮助-考虑将其提升为答案,以便我可以点赞! - davnicwil
1
@davnicwil:仅提供链接的回答很无聊,而且我没有心情重新表述已经在链接中提供的内容 :-) - zerkms
如果您只想获取用户在文本字段中输入的数据,并在他完成输入后进行操作,您可以使用“change”事件。 - Jithesh
我只是以按键事件作为快速触发事件的一个例子 - 这是关于限制事件处理到一定间隔的通用问题。 - davnicwil
3个回答

32

jQuery有内置的方法可以做到这一点吗?

没有。

如果没有,那么有没有更好的方式在原生JS中完成这个功能,比我在第二个jsfiddle示例中使用的方式更好?

你可以跟踪处理程序最后一次被调用的时间,并仅在经过一定的时间间隔后再调用它。这称为节流,有多种实现方式。

最简单的形式是在lastCall + interval时间内忽略每个调用,以便每个时间间隔内只发生一次调用。

例如,这里有一个函数,它返回一个新函数,每X毫秒只能调用一次:

function throttle(func, interval) {
    var lastCall = 0;
    return function() {
        var now = Date.now();
        if (lastCall + interval < now) {
            lastCall = now;
            return func.apply(this, arguments);
        }
    };
}

你可以将其用作

$("#inputField").on("keypress", throttle(function(event) {
   $("div#output").append("key pressed <br/>");  
}, 500));

演示


Esailija在他的评论中提到的那样,也许你需要的不是节流而是防抖。这是一个类似但略有不同的概念。虽然 节流 意味着某事只应该每 x 毫秒发生一次,而防抖 意味着某事只应该在过去的 x 毫秒内没有发生时才会发生。

一个典型的例子是scroll事件。滚动事件处理程序将被频繁调用,因为事件基本上是连续触发的。但也许您只想在用户停止滚动时执行处理程序,而不是 他滚动时执行。

实现这一点的一种简单方法是使用超时,并在再次调用函数时取消它(如果尚未运行超时):

function debounce(func, interval) {
    var lastCall = -1;
    return function() {
        clearTimeout(lastCall);
        var args = arguments;
        var self = this;
        lastCall = setTimeout(function() {
            func.apply(self, args);
        }, interval);
    };
}

这种实现的缺点是无法从事件处理程序中返回值(例如 return false;)。如果需要,可能会有一些可以保留此功能的实现。

演示


1
这不是限流吗?我认为去抖动意味着只要在某些毫秒内调用不断进来,就会不断移动超时,并在最终稳定下来时仅进行一次调用。也许我搞反了…… - Esailija
1
好像不是节流(throttling),也不是去抖(debouncing)。根据这个解释:http://drupalmotion.com/article/debounce-and-throttle-visual-explanation,可能是节流...啊但肯定不是去抖。 - Esailija
1
@davnicwil 确定你的应用程序做什么也会有所帮助,因为去抖可能更合适。例如,我从未在js中需要过节流...假设您每次按键都发送ajax请求。那么,在用户仍在输入时进行节流将浪费请求。 - Esailija
1
@Esailija:事件是在一系列事件的开始触发的,而不是在结束时触发。请参见http://jsfiddle.net/mKx2K/7。也许有一种方法可以不用`setTimeout`来实现,但我目前想不到(已经很晚了,我应该睡觉了;))。 - Felix Kling
1
@davnicwil:对用户体验和人类反应时间进行了大量研究,每当两个事件在彼此之间的100毫秒间隔内发生时,它们被认为同时发生。也就是说,它们无法区分。你可以称其为黑客技巧,也可以称其为应用科学;不管怎样,不用谢! :) - Felix Kling
显示剩余9条评论

2
我会将事件节流简化为以下方式:

我会将事件节流简化为以下方式:

$("#inputField").on("keypress", function(event) {
    var now = Date.now();
    var nt = $(this).data("lastime") || now; 
    if( nt > now ) return;
    $(this).data("lastime", now + 500);  
    $("div#output").append("key pressed <br/>");  
});

1
记录上一次处理事件的时间,每当您收到一个事件时,请检查所需间隔是否已经过去。

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