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

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

我想有一个延迟,所以它只在用户停止输入200毫秒时进行搜索。

keyup函数中没有这个选项,我尝试过setTimeout,但它不起作用。

我怎么能那么做?

337514 次浏览

看看自动完成插件。我知道它允许您指定延迟或最小字符数。即使您最终没有使用该插件,查看代码也会为您提供一些关于如何自己实现它的想法。

如果您想在类型完成后搜索,请使用全局变量来保存从setTimout调用返回的超时,如果它还没有发生,则使用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);
}

使用

mytimeout = setTimeout( expression, timeout );

其中表达式是要运行的脚本,超时是在运行之前等待毫秒的时间-这不会影响脚本,而是简单地延迟该部分的执行,直到超时完成。

clearTimeout(mytimeout);

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

我将这个小函数用于相同的目的,在用户停止键入指定时间后或在高速率触发的事件中执行函数,例如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)
}
}

该实现包含一套测试

对于更复杂的东西,请查看jQueryTypewatch插件。

您还可以查看underscore.js,它提供了像防抖这样的实用方法:

var lazyLayout = _.debounce(calculateLayout, 300);
$(window).resize(lazyLayout);

使用绑定与延迟相关 jQuery插件:

element.bindWithDelay(eventType, [ eventData ], handler(eventObject), timeout, throttle)

根据CMS的回答,我做了这个:

将下面的代码放在包含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)变量与输入不匹配

延迟函数调用每个键。 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 );

此函数对G的答案中的函数进行了一点扩展,以取回元素:

$.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/

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

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

请参阅下面的代码,我是根据其他答案编写的:

(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的建议将元素的引用传递给回调。

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

这是一个与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和输入名称生成的,因此它们必须是定义且唯一的,或者您可以根据您的情况对其进行调整。
  • 延迟返回一个易于根据自己的需要扩展的对象。
  • 用于延迟的原始元素绑定到回调,因此this按预期工作(如示例所示)。
  • 在第二次验证中忽略空值。
  • 注意入队,它首先需要毫秒,我更喜欢这样,但您可能希望切换参数以匹配setTimeout

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

基于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 );




})

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

$('#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);
});

对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);});
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
};

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

像这样做你的查询:

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

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

此函数获取输入字段的Object,放入您的代码中

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

使用标签延迟多功能调用

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

因此,结果是实际只会调用最后一次单击/操作,因为这些请求存储在队列中,如果队列中没有其他具有相同标签的请求,则在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)。并以“闭包方式”发送您的函数参数。

例如,如果您想调用Bellow函数:

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

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

如果有人喜欢延迟相同的函数,并且没有外部变量,他可以使用下一个脚本:

function MyFunction() {


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


//Execute the function code here...


}, 500);
}

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');
});

补充说明

使用一个变量来存储超时函数。然后使用clearTimeout()清除该变量的任何活动超时函数,然后使用setTimeout()再次设置活动超时函数。我们先运行clearTimeout(),因为如果用户正在输入“hello”,我们希望我们的函数在用户按下“o”键后不久运行(而不是每个字母一次)。

工作演示

超级简单的方法,旨在在用户完成输入文本字段后运行函数…

    $(document).ready(function(e) {
var timeout;
var delay = 2000;   // 2 seconds


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


function myFunction() {
$('#status').html("Executing function for user!");
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Status: <span id="status">Default Status</span><br>
<textarea name="text-input" class="text-input"></textarea>

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

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


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

基于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);
});

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

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

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

更新的打字稿版本:

const delayKeyUp = (() => {
let timer: NodeJS.Timeout;
return (func: Function, ms: number) => {
timer ? clearTimeout(timer) : null;
timer = setTimeout(() => func(), ms);
};
})();

用户豆沙 javascript库并使用_防抖功能

changeName: _.debounce(function (val) {
console.log(val)
}, 1000)
// 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);
}

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..."/>

如果你想在一段时间后做一些事情,并在像keyup这样的特定事件后重置计时器,最好的解决方案是使用clearTimeoutsetTimeout方法:

// declare the timeout variable out of the event listener or in the global scope
var timeout = null;


$(".some-class-or-selector-to-bind-event").keyup(function() {
clearTimeout(timout); // this will clear the recursive unneccessary calls
timeout = setTimeout(() => {
// do something: send an ajax or call a function here
}, 2000);
// wait two seconds


});