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

时间:2009-12-15 18:33:27

标签: javascript jquery

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

我希望有延迟,因此只会在用户停止输入200毫秒时进行搜索。

keyup函数中没有此选项,我尝试了setTimeout,但它没有用。

我该怎么做?

27 个答案:

答案 0 :(得分:1025)

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

实施涵盖set of tests

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

答案 1 :(得分:56)

如果要在类型完成后进行搜索,请使用全局变量来保存从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);  
}   

答案 2 :(得分:30)

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

答案 3 :(得分:28)

您还可以查看underscore.js,其中提供了debounce等实用程序方法:

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

答案 4 :(得分:14)

根据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)变量与输入

不匹配

答案 5 :(得分: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);
  }

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

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

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

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

答案 6 :(得分:6)

这个函数从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/

答案 7 :(得分:6)

超级简单方法,用于在用户输入文本字段后运行函数...

<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>

答案 8 :(得分: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);
});

答案 9 :(得分:6)

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

function MyFunction() {

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

        //Execute the function code here...

    }, 500);
}

答案 10 :(得分: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引用保留在input的delayTimer变量中。它还将元素的引用传递给fazzyx建议的回调。

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

答案 11 :(得分:5)

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

答案 12 :(得分: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 );

答案 13 :(得分:3)

建立在CMS的答案这里的新延迟方法,保留了这个&#39;在其用法中:

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

答案 14 :(得分:2)

使用

mytimeout = setTimeout( expression, timeout );

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

clearTimeout(mytimeout);

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

答案 15 :(得分:2)

CMS answerMiguel's one相结合,可以提供一个可以实现并发延迟的强大解决方案。

LS_CONF_DIR=/etc/logstash/conf.d
...
program=/opt/logstash/bin/logstash
args="agent -f ${LS_CONF_DIR} -l ${LS_LOG_FILE} ${LS_OPTS}"

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

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

答案 16 :(得分: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 );


})

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

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

答案 18 :(得分:1)

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

此函数获取输入字段的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);
});

答案 19 :(得分: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
};

答案 20 :(得分:1)

使用bindWithDelay jQuery插件:

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

答案 21 :(得分:0)

如果您想在一段时间后做某事并在特定事件(例如 keyup)后重置该计时器,最好的解决方案是使用 clearTimeoutsetTimeout 方法:

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

$("#some-id-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

});

答案 22 :(得分: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);
}

答案 23 :(得分:0)

用户lodash JavaScript库并使用_.debounce函数

changeName: _.debounce(function (val) {
  console.log(val)                
}, 1000)

答案 24 :(得分:0)

ES6 中,也可以使用arrow function syntax

在此示例中,代码在用户完成键入后将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);
})

答案 25 :(得分: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 */ });
});

答案 26 :(得分:0)

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