在jQuery中限制事件调用

Throttle event calls in jQuery

本文关键字:事件 调用 jQuery      更新时间:2023-09-26

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

$("#search").keyup(function() {
  //code that takes a little bit to complete
});

当用户键入一个完整的单词或快速按下键时,该函数将被连续调用几次,并且需要一段时间才能全部完成。

是否有一种方法来限制事件调用,以便如果有几个快速连续,它只触发最近调用的那个?

看看jQuery Debounce。

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

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

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

您也可以使用优秀的Underscore/_库。

在Josh的回答中,目前最受欢迎的评论是,你是否应该真的限制调用,或者如果你想要一个debouncer。区别有点微妙,但下划线同时具有:_.debounce(function, wait, [immediate])_.throttle(function, wait, [options])

如果你还没有使用下划线,检查一下。它可以使你的JavaScript更干净,并且足够轻量,让大多数讨厌库的人停下来。

这是使用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);

节流方法的两个小型通用实现。(我更喜欢通过这些简单的函数来完成,而不是添加另一个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);
        }
      }
    }
    

    用法很简单:

    下一个在inputBox中最后一次击键后等待2s,然后调用应该被节流的函数。

    $("#inputBox").on("input", throttle(2000, function(evt) {
      myFunctionToThrottle(evt);
    }));
    
    下面是一个可以同时测试两者的例子:点击(CodePen)

    我在审查zurb-foundation的更改时遇到了这个问题。他们还添加了自己的解封和节流方法。这看起来可能和他在回答中提到的jquery-debounce @josh3736是一样的。

    来自他们的网站:

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

    对于一个快速解决方案(注意coffeescript),像这样的东西似乎是最简单的(没有外部库):

    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