ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

我怎样才能做到这一点?

如果可以,我会将其作为副本关闭。
只要给出和接受的答案是正确的,我看不到重复的危害。将问题添加到数据库中必须是一件好事,也是一件值得努力的事情。
危害是,如果有 100 个相同的问题,未来的人们将无法从每个人都分享的精彩答案中受益,因此关闭欺骗并将每个人重定向到原始问题对于找到最佳实践和修复更好。有关关闭重复项的原因的详细信息,请参阅 stackoverflow.com/help/duplicates
这比它应该复制的那个更受欢迎。它的措辞更好,答案更好,在谷歌等上排名更高。很多人都从这个答案中受益。回想起来,如果关闭,那将是一种耻辱。有一些琐碎的重复项很糟糕,但这不属于该类别。

C
Community

我将这个小函数用于相同的目的,在用户停止输入指定的时间或在高速触发的事件(如 resize)中执行一个函数:

函数延迟(回调,毫秒){ var timer = 0; return function() { var context = this, args = arguments;清除超时(定时器); timer = setTimeout(function () { callback.apply(context, args); }, ms || 0); }; } // 示例用法: $('#input').keyup(delay(function (e) { console.log('Time elapsed!', this.value); }, 500));

这个怎么运作:

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 插件。


另一种选择:github.com/bgrins/bindWithDelay/blob/master/bindWithDelay.js。它的工作方式与您描述的几乎相同,我只是发现自己经常使用该模式,因此将其实现为 jQuery 插件以使语法更简单。这是一个演示页面:briangrinstead.com/files/bindWithDelay
如果用户按下回车键,如何立即执行回调函数而不延迟?
A
Alain Tiemblo

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

P
Paschover

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

e
emartini

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

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

H
HoldOffHunger

解释

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

工作演示

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

$(document).ready(function(e) { var timeout; var delay = 2000; // 2 秒 $('.text-input').keyup(function(e) { $('#status').html ("用户开始输入!"); if(timeout) { clearTimeout(timeout); } timeout = setTimeout(function() { myFunction(); }, delay); }); function myFunction() { $('#status ').html("正在为用户执行函数!"); } }); 状态:默认状态


G
Gaten

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


M
Miguel

使用标签延迟多功能调用

这是我使用的解决方案。它将延迟您想要的任何功能的执行。它可以是 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);});

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


这真的很好用。谢谢。
R
Roy Shoa

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

function MyFunction() {

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

        //Execute the function code here...

    }, 500);
}

A
Ali

jQuery:

var超时=空; $('#input').keyup(function() { clearTimeout(timeout); timeout = setTimeout(() => { console.log($(this).val()); }, 1000); });

纯Javascript:

让输入 = document.getElementById('input');让超时=空; input.addEventListener('keyup', function (e) { clearTimeout(timeout); timeout = setTimeout(function () { console.log('Value:', input.value); }, 1000); });


D
Dervall

此函数扩展了 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/


j
jszoja

我很惊讶没有人提到 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 的建议将元素的引用传递给回调。

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


已经尝试过了,但在第一次键盘输入时无法正常工作。
S
Sagar Gala

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

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

没有按预期工作 - 提取次数相同,只是延迟了 300 毫秒 - 在执行 setTimeout() 之前,您需要在每次击键时使用 clearTimeout() 函数
好吧,在这种情况下,您不需要使用 clearTimeout。 setTimeout 中的条件 searchValue == $('#searchText').val() 将确保该值是否与 300 毫秒前的值相同。让我知道这是否有意义。谢谢。
V
Vinay Aggarwal

延迟功能可在每次按键时调用。需要 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 );


D
Dat

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

更新的打字稿版本:

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

H
Hanuman

这是一个类似于 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 返回一个易于扩展以满足您自己需求的对象。

用于延迟的原始元素绑定到回调,因此可以按预期工作(如示例中所示)。

在第二次验证中将忽略空值。

注意入队,它首先需要毫秒,我更喜欢这样,但您可能希望切换参数以匹配 setTimeout。

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


F
Frédéric

CMS answerMiguel's one 相结合产生了一个允许并发延迟的稳健解决方案。

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

m
mga911

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

M
Mark Schultheiss

利用

mytimeout = setTimeout( expression, timeout );

其中表达式是要运行的脚本,超时是在运行前等待的时间(以毫秒为单位)——这不会拖拽脚本,而只是延迟该部分的执行,直到超时完成。

clearTimeout(mytimeout);

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


A
Anderson

根据 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 );


})

V
Vebjorn Ljosa

使用 bindWithDelay jQuery 插件:

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

k
krut1
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
};

e
egpo

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

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

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

A
Amir Ur Rehman

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

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

S
Samir Rahimy

如果您想在一段时间后做某事并在特定事件(如 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

});

t
tvanfosson

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


佚名

好吧,我还编写了一段代码来限制由 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 */ });
});

c
chungtinhlakho

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

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

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

B
Bharat chaudhari
// 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);
}