在jQuery中限制事件调用次数

45

我绑定了一个 keyup 事件到一个需要大约四分之一秒才能完成的函数。

$("#search").keyup(function() {
  //code that takes a little bit to complete
});
当用户输入完整的单词或快速按键时,该函数将被连续调用多次,并且需要一段时间才能完成所有调用。
是否有一种方法可以限制事件调用的频率,以便如果有多个事件在短时间内触发,它只会触发最近调用的一个?

2
这是一个非常有用的高级JS主题,更多人应该看到它。 - th3byrdm4n
1
关于使用节流器(throttle)还是去抖动器(debouncer)的问题存在争议。请参考此链接:https://dev59.com/VGs05IYBdhLWcg3wPPWB#40268544 - Michael Scheper
与下面的回答相比,这个问题有一个更好的答案。https://dev59.com/AGox5IYBdhLWcg3wHAwA - dangel
7个回答

69

看一下jQuery Debounce

$('#search').keyup($.debounce(function() {
    // Will only execute 300ms after the last keypress.
}, 300));

1
当我使用多个选择器,例如 .sel1, .sel2 sel3 时,这个不起作用。 - HungryCoder
2
@HungryCoder:它确实有效。 当然,每个匹配的元素将共享相同的计时器,因此回调函数直到您完全停止发送事件后才会触发。 - josh3736
4
去抖并不是节流,它是另一种东西。 - vsync
@josh3736 - 在这种情况下,您应该编辑他的帖子和标题,以便来到这里的人不会感到困惑。 - vsync
8
这并没有回答问题。原帖的作者可能认为已经得到了答案,但实际上这甚至还不到所描述的一半,并且根本不符合自动完成结果的预期行为。防抖函数不是正确的选择。原帖的作者实际上是在询问节流函数。请参考:https://css-tricks.com/the-difference-between-throttling-and-debouncing/ - Aeschylus
显示剩余5条评论

13

这里有一个潜在的解决方案,不需要插件。使用一个布尔值来决定是否执行keyup回调函数,或者跳过它。

var doingKeyup = false;

$('input').keyup(function(){
    if(!doingKeyup){
        doingKeyup=true;
        // slow process happens here
        doingKeyup=false;
    }
});

2
这是一个限流器,而不是防抖器。对于搜索字段,您希望在用户完成输入后运行搜索(防抖),而不是在他们输入一个字符后运行(限流)。防抖插件实际上只是一个<1 kB的便捷方法,它将调用setTimeout / clearTimeout进行包装。 - josh3736
1
我不会对他的搜索字段预设做任何假设。 - Nathan Manousos
1
@josh3736 说实话,问题标题确实要求“节流”。不过我之前还没听说过“去抖动”这个概念。 - Peter Olson
1
这种方法还存在一个问题——如果在上一个处理程序运行时输入了最后一个字符或一系列字符,则不会触发它。 - Michael Teper
2
只有当// slow process发生在另一个事件循环中时,这才是正确的方式。否则,由于JS是单线程的,这是没有用的。 - Ashish Negi
显示剩余2条评论

6

2
这里有一个使用JQuery的简洁方法。
    /* delayed onchange while typing jquery for text boxes widget
    usage:
        $("#SearchCriteria").delayedChange(function () {
            DoMyAjaxSearch();
        });

    */
    (function ($) {
        $.fn.delayedChange = function (options) {
            var timer;
            var o;

            if (jQuery.isFunction(options)) {
                o = { onChange: options };
            }
            else
                o = options;

            o = $.extend({}, $.fn.delayedChange.defaultOptions, o);

            return this.each(function () {
                var element = $(this);
                element.keyup(function () {
                    clearTimeout(timer);
                    timer = setTimeout(function () {
                        var newVal = element.val();
                        newVal = $.trim(newVal);
                        if (element.delayedChange.oldVal != newVal) {
                            element.delayedChange.oldVal = newVal;
                            o.onChange.call(this);
                        }

                    }, o.delay);
                });
            });


        };

        $.fn.delayedChange.defaultOptions = {
            delay: 1000,
            onChange: function () { }
        }

        $.fn.delayedChange.oldVal = "";


    })(jQuery);

1

两种简单的限流方法的通用实现。(我更喜欢通过这些简单的函数来实现,而不是添加另一个jquery插件)

  1. 在最后一次调用后等待一段时间

    当用户持续输入查询时,这个方法非常有用,我们不想每次都调用搜索功能。

function throttle(time, func) {
  if (!time || typeof time !== "number" || time < 0) {
      return func;
  }

  var throttleTimer = 0;

  return function() {
    var args = arguments;
    clearTimeout(throttleTimer);
    throttleTimer = setTimeout(function() {
      func.apply(null, args);
    }, time);
  }
}
  • 不超过给定时间调用给定函数

    以下内容对于清除日志非常有用

  • function throttleInterval(time, func) {
      if (!time || typeof time !== "number" || time < 0) {
          return func;
      }
    
      var throttleTimer = null;
      var lastState = null;
      var eventCounter = 0;
      var args = [];
    
      return function() {
        args = arguments;
        eventCounter++;
        if (!throttleTimer) {
          throttleTimer = setInterval(function() {
            if (eventCounter == lastState) {
              clearInterval(throttleTimer);
              throttleTimer = null;
              return;
            }
    
            lastState = eventCounter;
            func.apply(null, args);
          }, time);
        }
      }
    }
    

    使用非常简单:

    在输入框最后一次按键后等待2秒,然后调用应该被节流的函数。

    $("#inputBox").on("input", throttle(2000, function(evt) {
      myFunctionToThrottle(evt);
    }));
    

    这是一个示例,您可以在其中测试两者:点击(CodePen)

    这两个例子都是防抖方法,不是节流方法 ;) - Chaoste

    -1

    我在审查的更改时遇到了这个问题。他们添加了自己的方法用于防抖和节流。看起来这可能与@josh3736在他的答案中提到的jquery-debounce相同。

    从他们的网站上得知:

    // Debounced button click handler
    $('.button').on('click', Foundation.utils.debounce(function(e){
      // Handle Click
    }, 300, true));
    
    // Throttled resize function
    $(document).on('resize', Foundation.utils.throttle(function(e){
      // Do responsive stuff
    }, 300));
    

    -4

    对于快速解决问题(注意咖啡脚本),以下代码似乎是最简单的选择(无需外部库):

    running = false
    $(document).on 'keyup', '.some-class', (e) ->
      return if running
      running = true
      $.ajax
        type: 'POST',
        url: $(this).data('url'),
        data: $(this).parents('form').serialize(),
        dataType: 'script',
        success: (data) ->
          running = false
    

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