如何延迟.keyup()处理函数,直到用户停止键入?


641

我有一个搜索字段。现在,它会搜索每个键。因此,如果有人键入“ Windows”,它将使用AJAX搜索每个键盘:“ W”,“ Wi”,“ Win”,“ Wind”,“ Windo”,“ Window”,“ Windows”。

我希望有一个延迟,因此它仅在用户停止键入200毫秒时才搜索。

keyup函数中没有为此的选项,我已经尝试过了setTimeout,但是没有用。

我怎样才能做到这一点?



2
如果可以的话,我将其关闭。
2009年

7
只要给出和接受的答案是正确的,我看不到重复的危害。将问题添加到数据库中必须是一件好事,并且需要努力。
马蒂斯(Mattis)

14
害处在于,如果有100个相同的问题,将来的人们将无法从每个人共享的绝妙答案中受益,因此关闭重复项并将每个人重定向到原始问题会更好地寻找最佳实践和解决方案。有关为什么关闭重复项的更多信息,请参见stackoverflow.com/help/duplicates
rncrtr

14
这种方式比原本应该复制的方式更受欢迎。它的措辞更好,答案更好,在google上的排名更高。等等。回想起来,如果不这样做,那将是一种耻辱。有一些琐碎的东西作为复制品是不好的,但这不属于该类别。
用户

Answers:


1120

我将这个小功能用于相同的目的,即在用户停止输入指定的时间后或在触发率很高的事件中执行一个功能,例如resize

function delay(callback, ms) {
  var timer = 0;
  return function() {
    var context = this, args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function () {
      callback.apply(context, args);
    }, ms || 0);
  };
}


// Example usage:

$('#input').keyup(delay(function (e) {
  console.log('Time elapsed!', this.value);
}, 500));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label for="input">Try it:
<input id="input" type="text" placeholder="Type something here..."/>
</label>

怎么运行的:

delay函数将返回一个包装的函数,该函数在内部处理一个单独的计时器,在每次执行中,计时器都将按提供的时间延迟重新启动,如果在此时间过去之前发生了多次执行,则计时器将重置并重新启动。

当计时器最终结束时,将执行回调函数,并传递原始上下文和参数(在此示例中,jQuery的事件对象以及DOM元素为this)。

更新2019-05-16

我已经针对现代环境使用ES5和ES6功能重新实现了该功能:

function delay(fn, ms) {
  let timer = 0
  return function(...args) {
    clearTimeout(timer)
    timer = setTimeout(fn.bind(this, ...args), ms || 0)
  }
}

该实现包含一组测试

对于更复杂的内容,请看一下jQuery Typewatch插件。


64
另一种选择:github.com/bgrins/bindWithDelay/blob/master/bindWithDelay.js。它的工作方式几乎与您描述的相同,我发现自己经常使用该模式,因此将其实现为jQuery插件以简化语法。这是一个演示页面:briangrinstead.com/files/bindWithDelay
Brian Grinstead

2
我最初对键入的字母数量进行了硬编码,这种方法很容易处理,就像糖蜜一样(没有硫磺糖)
2012年

2
当我将其与$ .post()一起使用时,它会同时发送所有键入的内容,但它会发送与键入内容相同的次数。有没有办法仅在超时结束时发送?
ntgCleaner 2012年

7
但是,如果我调用两个或多个需要单独延迟的不同功能,则此方法不起作用。我改用了此解决方案stackoverflow.com/a/30503848/1834212
Miguel

4
我建议在顶部评论中链接“ bindWithDelay”插件。但我建议@ Hazerider的回答这个问题(如下- stackoverflow.com/a/19259625/368896),这是比这更好的答案,值得学习,了解背后的想法简单,圆滑的结合,允许不同的元素不同的定时器-在上面插件代码中使用的原理相同,但更易于理解。
丹·尼森鲍姆

60

如果您要在类型完成后进行搜索,请使用全局变量来保存从您的setTimout调用返回的超时,并使用a来取消超时(clearTimeout如果尚未发生),这样就不会触发超时,除非最后一个keyup事件

var globalTimeout = null;  
$('#id').keyup(function(){
  if(globalTimeout != null) clearTimeout(globalTimeout);  
  globalTimeout =setTimeout(SearchFunc,200);  
}   
function SearchFunc(){  
  globalTimeout = null;  
  //ajax code
}

或具有匿名功能:

var globalTimeout = null;  
$('#id').keyup(function() {
  if (globalTimeout != null) {
    clearTimeout(globalTimeout);
  }
  globalTimeout = setTimeout(function() {
    globalTimeout = null;  

    //ajax code

  }, 200);  
}   

39

CMS答案的另一个小改进。要轻松考虑单独的延迟,可以使用以下方法:

function makeDelay(ms) {
    var timer = 0;
    return function(callback){
        clearTimeout (timer);
        timer = setTimeout(callback, ms);
    };
};

如果要重复使用相同的延迟,只需执行

var delay = makeDelay(250);
$(selector1).on('keyup', function() {delay(someCallback);});
$(selector2).on('keyup', function() {delay(someCallback);});

如果您想要单独的延迟,可以执行

$(selector1).on('keyup', function() {makeDelay(250)(someCallback);});
$(selector2).on('keyup', function() {makeDelay(250)(someCallback);});

1
有趣的是,截至本文撰写之日为止,具有600多个投票的解决方案不如只有2个投票(包括我的投票)的答案好。显然优越,因为它支持可以共享或不共享延迟的多个小部件。极好的答案。
Dan Nissenbaum 2015年

...尽管答案下方的注释中的jQuery插件也处理单独元素的单独计时器,并且还添加了节流阀和事件参数:github.com/bgrins/bindWithDelay/blob/master/bindWithDelay.js。该插件的一般方法与您的代码相同,因此在尝试理解更复杂的插件之前,值得仔细研究您的代码,以使其简单易懂。我建议使用该插件-但是,如果您不需要限制或事件数据的传递,则您的代码很棒!两个不错的选择。谢谢!
Dan Nissenbaum 2015年

1
嗯,它确实对我someApiCall有所延迟,但仍然会触发多次-现在只是输入字段的最终值。
Roelant


14

根据CMS的答案,我做到了:

将以下代码放在include jQuery之后:

/*
 * delayKeyup
 * http://code.azerti.net/javascript/jquery/delaykeyup.htm
 * Inspired by CMS in this post : http://stackoverflow.com/questions/1909441/jquery-keyup-delay
 * Written by Gaten
 * Exemple : $("#input").delayKeyup(function(){ alert("5 secondes passed from the last event keyup."); }, 5000);
 */
(function ($) {
    $.fn.delayKeyup = function(callback, ms){
        var timer = 0;
        $(this).keyup(function(){                   
            clearTimeout (timer);
            timer = setTimeout(callback, ms);
        });
        return $(this);
    };
})(jQuery);

只需这样使用:

$('#input').delayKeyup(function(){ alert("5 secondes passed from the last event keyup."); }, 5000);

注意:函数中作为参数传递的$(this)变量与输入不匹配


11

使用标签延迟多功能通话

这是我使用的解决方案。它将延迟您想要的任何功能的执行。它可以是keydown搜索查询,也可以是快速单击上一个或下一个按钮(否则,如果连续快速单击,则将发送多个请求,而根本不会使用)。这使用一个全局对象来存储每个执行时间,并将其与最新请求进行比较。

因此,结果是只有那些最后的单击/操作才会被实际调用,因为这些请求存储在队列中,如果在队列中不存在其他具有相同标签的请求,则在X毫秒之后将被调用!

function delay_method(label,callback,time){
    if(typeof window.delayed_methods=="undefined"){window.delayed_methods={};}  
    delayed_methods[label]=Date.now();
    var t=delayed_methods[label];
    setTimeout(function(){ if(delayed_methods[label]!=t){return;}else{  delayed_methods[label]=""; callback();}}, time||500);
  }

您可以设置自己的延迟时间(其可选时间,默认为500ms)。并以“关闭方式”发送函数参数。

例如,如果要调用波纹管功能:

function send_ajax(id){console.log(id);}

为了防止出现多个send_ajax请求,可以使用以下方法延迟它们

delay_method( "check date", function(){ send_ajax(2); } ,600);

只有在600毫秒的时间范围内没有其他请求时,才会触发使用标签“检查日期”的每个请求。此参数是可选的

标签独立性(调用相同的目标函数),但同时运行两个:

delay_method("check date parallel", function(){send_ajax(2);});
delay_method("check date", function(){send_ajax(2);});

导致调用相同的函数,但由于其标签不同而使它们独立延迟


我在您正在研究的项目中使用您的代码,但遇到困难。它实际上并没有延迟任何事情。以为您可能想查看/提供帮助。 stackoverflow.com/questions/51393779/…–
KDJ

9

如果有人希望延迟相同的功能,并且没有外部变量,则可以使用下一个脚本:

function MyFunction() {

    //Delaying the function execute
    if (this.timer) {
        window.clearTimeout(this.timer);
    }
    this.timer = window.setTimeout(function() {

        //Execute the function code here...

    }, 500);
}

1
大!简单的解决方案,行之有效!
Fellipe Sanches

1
我喜欢这一个。不完全可重用,但易于理解。
文森特

9

超级简单的方法,旨在在用户输入完文本字段后运行功能...

<script type="text/javascript">
$(document).ready(function(e) {
    var timeout;
    var delay = 2000;   // 2 seconds

    $('.text-input').keyup(function(e) {
        console.log("User started typing!");
        if(timeout) {
            clearTimeout(timeout);
        }
        timeout = setTimeout(function() {
            myFunction();
        }, delay);
    });

    function myFunction() {
        console.log("Executing function for user!");
    }
});
</script>

<textarea name="text-input" class="text-input"></textarea>

1
谢谢!上面流行的答案没有用...但是您的建议效果很好。
user2662680

1
2020年仍然有效
Romel Indemne

7

该函数从Gaten的答案中扩展了该函数,以使元素返回:

$.fn.delayKeyup = function(callback, ms){
    var timer = 0;
    var el = $(this);
    $(this).keyup(function(){                   
    clearTimeout (timer);
    timer = setTimeout(function(){
        callback(el)
        }, ms);
    });
    return $(this);
};

$('#input').delayKeyup(function(el){
    //alert(el.val());
    // Here I need the input element (value for ajax call) for further process
},1000);

http://jsfiddle.net/Us9bu/2/


6

这对我有用,在这里我延迟了搜索逻辑操作,并检查该值是否与文本字段中输入的值相同。如果值相同,那么我将继续执行与搜索值相关的数据的操作。

$('#searchText').on('keyup',function () {
    var searchValue = $(this).val();
    setTimeout(function(){
        if(searchValue == $('#searchText').val() && searchValue != null && searchValue != "") {
           // logic to fetch data based on searchValue
        }
        else if(searchValue == ''){
           // logic to load all the data
        }
    },300);
});

无法按预期方式工作-提取次数相同,只是延迟了300ms-您需要在每次击键时都使用clearTimeout()函数,然后再执行setTimeout()
Picard

好吧,在这种情况下,您无需使用clearTimeout。setTimeout中的条件searchValue == $('#searchText')。val()将确保该值是否与300ms之前的值相同。让我知道这是否有意义。谢谢。
Sagar Gala

5

我很惊讶,没有人提到CMS非常好地剪掉多个输入的问题。

基本上,您必须为每个输入分别定义延迟变量。否则,如果sb将文本输入第一个输入并快速跳转到其他输入并开始输入,则第一个输入的回调将不会被调用!

请参阅以下基于其他答案的代码:

(function($) {
    /**
     * KeyUp with delay event setup
     * 
     * @link http://stackoverflow.com/questions/1909441/jquery-keyup-delay#answer-12581187
     * @param function callback
     * @param int ms
     */
    $.fn.delayKeyup = function(callback, ms){
            $(this).keyup(function( event ){
                var srcEl = event.currentTarget;
                if( srcEl.delayTimer )
                    clearTimeout (srcEl.delayTimer );
                srcEl.delayTimer = setTimeout(function(){ callback( $(srcEl) ); }, ms);
            });

        return $(this);
    };
})(jQuery);

此解决方案将setTimeout引用保留在输入的delayTimer变量内。还将fazzyx建议将element的引用传递给回调。

在IE6、8(comp-7),8和Opera 12.11中进行了测试。


尝试过此操作,但在第一次键入时无法正常工作。
拉尔斯·索伦(LarsThorén)2014年

4

延迟功能可在每次键入时调用。 需要jQuery 1.7.1或更高版本

jQuery.fn.keyupDelay = function( cb, delay ){
  if(delay == null){
    delay = 400;
  }
  var timer = 0;
  return $(this).on('keyup',function(){
    clearTimeout(timer);
    timer = setTimeout( cb , delay );
  });
}

用法: $('#searchBox').keyupDelay( cb );


3

在CMS的答案的基础上,这是一种新的延迟方法,该方法在使用中保留了“ this”:

var delay = (function(){
  var timer = 0;
  return function(callback, ms, that){
    clearTimeout (timer);
    timer = setTimeout(callback.bind(that), ms);
  };
})();

用法:

$('input').keyup(function() {
    delay(function(){
      alert('Time elapsed!');
    }, 1000, this);
});

2

采用

mytimeout = setTimeout( expression, timeout );

其中expression是要运行的脚本,超时是在运行之前等待的时间(以毫秒为单位)-这不会破坏脚本,而只是延迟了该部分的执行,直到超时为止。

clearTimeout(mytimeout);

将重置/清除超时,因此只要尚未执行,它就不会在表达式中运行脚本(如取消)。


2

这是CMS的解决方案,但为我解决了一些关键问题:

  • 支持多个输入,延迟可以同时运行。
  • 忽略没有更改值的键事件(例如Ctrl,Alt + Tab)。
  • 解决竞争条件(执行回调且值已更改时)。
var delay = (function() {
    var timer = {}
      , values = {}
    return function(el) {
        var id = el.form.id + '.' + el.name
        return {
            enqueue: function(ms, cb) {
                if (values[id] == el.value) return
                if (!el.value) return
                var original = values[id] = el.value
                clearTimeout(timer[id])
                timer[id] = setTimeout(function() {
                    if (original != el.value) return // solves race condition
                    cb.apply(el)
                }, ms)
            }
        }
    }
}())

用法:

signup.key.addEventListener('keyup', function() {
    delay(this).enqueue(300, function() {
        console.log(this.value)
    })
})

该代码以我喜欢的风格编写,您可能需要添加一堆分号。

注意事项:

  • 根据表单ID和输入名称生成唯一ID,因此必须定义它们并使其唯一,否则您可以根据情况进行调整。
  • delay返回一个易于扩展以满足您自己需求的对象。
  • 用于延迟的原始元素已绑定到回调,因此this按预期方式工作(如示例中所示)。
  • 空值将在第二次验证中被忽略。
  • 当心排队,我希望它首先等待毫秒,但您可能需要切换参数以匹配setTimeout

我使用的解决方案增加了另一层次的复杂性,例如,允许您取消执行,但这是一个良好的基础。


2

根据CMS的答案,它只忽略不会更改值的关键事件。

var delay = (function(){
    var timer = 0;
    return function(callback, ms){
      clearTimeout (timer);
      timer = setTimeout(callback, ms);
    };
})(); 

var duplicateFilter=(function(){
  var lastContent;
  return function(content,callback){
    content=$.trim(content);
    if(content!=lastContent){
      callback(content);
    }
    lastContent=content;
  };
})();

$("#some-input").on("keyup",function(ev){

  var self=this;
  delay(function(){
    duplicateFilter($(self).val(),function(c){
        //do sth...
        console.log(c);
    });
  }, 1000 );


})

2

CMS答案Miguel的答案相结合,可以生成一个健壮的解决方案,并发延迟。

var delay = (function(){
    var timers = {};
    return function (callback, ms, label) {
        label = label || 'defaultTimer';
        clearTimeout(timers[label] || 0);
        timers[label] = setTimeout(callback, ms);
    };
})();

当您需要独立延迟其他操作时,请使用第三个参数。

$('input.group1').keyup(function() {
    delay(function(){
        alert('Time elapsed!');
    }, 1000, 'firstAction');
});

$('input.group2').keyup(function() {
    delay(function(){
        alert('Time elapsed!');
    }, 1000, '2ndAction');
});


1
var globalTimeout = null;  
$('#search').keyup(function(){
  if(globalTimeout != null) clearTimeout(globalTimeout);  
  globalTimeout =setTimeout(SearchFunc,200);  
});
function SearchFunc(){  
  globalTimeout = null;  
  console.log('Search: '+$('#search').val());
  //ajax code
};

1

这是我写的建议,可以处理您表单中的多个输入。

此函数获取输入字段的对象,并将其放入代码中

function fieldKeyup(obj){
    //  what you want this to do

} // fieldKeyup

这是实际的delayCall函数,负责多个输入字段

function delayCall(obj,ms,fn){
    return $(obj).each(function(){
    if ( typeof this.timer == 'undefined' ) {
       // Define an array to keep track of all fields needed delays
       // This is in order to make this a multiple delay handling     
          function
        this.timer = new Array();
    }
    var obj = this;
    if (this.timer[obj.id]){
        clearTimeout(this.timer[obj.id]);
        delete(this.timer[obj.id]);
    }

    this.timer[obj.id] = setTimeout(function(){
        fn(obj);}, ms);
    });
}; // delayCall

用法:

$("#username").on("keyup",function(){
    delayCall($(this),500,fieldKeyup);
});

1

ES6开始,也可以使用箭头函数语法

在此示例中,代码keyup在用户完成键入后将事件延迟400毫秒,然后才调用searchFunc发出查询请求。

const searchbar = document.getElementById('searchBar');
const searchFunc = // any function

// wait ms (milliseconds) after user stops typing to execute func
const delayKeyUp = (() => {
    let timer = null;
    const delay = (func, ms) => {
        timer ? clearTimeout(timer): null
        timer = setTimeout(func, ms)
    }
    return delay
})();

searchbar.addEventListener('keyup', (e) => {
    const query = e.target.value;
    delayKeyUp(() => {searchFunc(query)}, 400);
})

1

jQuery的

var timeout = null;
$('#input').keyup(function() {
  clearTimeout(timeout);
  timeout = setTimeout(() => {
      console.log($(this).val());
  }, 1000);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<input type="text" id="input" placeholder="Type here..."/>

纯Javascript:

let input = document.getElementById('input');
let timeout = null;

input.addEventListener('keyup', function (e) {
    clearTimeout(timeout);
    timeout = setTimeout(function () {
        console.log('Value:', input.value);
    }, 1000);
});
<input type="text" id="input" placeholder="Type here..."/>


0

看一下自动完成插件。我知道,它允许您指定延迟或最少字符数。即使您最终没有使用该插件,也可以通过浏览代码来了解如何自己实现该插件。


0

好吧,我还编写了一段代码来限制由Keyup / Keydown引起的高频ajax请求。看一下这个:

https://github.com/raincious/jQueue

这样查询吗?

var q = new jQueue(function(type, name, callback) {
    return $.post("/api/account/user_existed/", {Method: type, Value: name}).done(callback);
}, 'Flush', 1500); // Make sure use Flush mode.

并这样绑定事件:

$('#field-username').keyup(function() {
    q.run('Username', this.val(), function() { /* calling back */ });
});

0

今天看到这个有点晚了,但是只是想把它放在这里,以防其他人需要。只需将函数分开以使其可重用。输入停止后,下面的代码将等待1/2秒。

    var timeOutVar
$(selector).on('keyup', function() {

                    clearTimeout(timeOutVar);
                    timeOutVar= setTimeout(function(){ console.log("Hello"); }, 500);
                });


0
// Get an global variable isApiCallingInProgress

//  check isApiCallingInProgress 
if (!isApiCallingInProgress) {
// set it to isApiCallingInProgress true
      isApiCallingInProgress = true;

      // set timeout
      setTimeout(() => {
         // Api call will go here

        // then set variable again as false
        isApiCallingInProgress = false;     
      }, 1000);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.