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

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

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

我该怎么办?

评论

复制了stackoverflow.com/questions/1620602/…

如果可以的话,我将其作为副本关闭。

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

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

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

#1 楼

我将这个小功能用于相同的目的,即在用户停止输入指定的时间后或在触发率很高的事件中执行一个功能,例如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插件。

评论


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

–布赖恩·格林斯特德(Brian Grinstead)
2010年8月13日14:31



我最初对键入的字母数量进行了硬编码,这种方法像糖蜜一样平滑(没有硫磺)

– Har
2012-2-28 15:35

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

–ntgCleaner
2012年6月13日在20:24

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

–米格尔
15年5月29日在16:22

我建议在顶部评论中链接“ bindWithDelay”插件。但是我建议@Hazerider对这个问题的答案(在下面-stackoverflow.com/a/19259625/368896),比这个答案更好,并且值得研究以理解简单,精巧的绑定背后的思想,以便为不同的元素使用不同的计时器-上面插件代码中使用的原理相同,但更易于理解。

–丹·尼森鲍姆(Dan Nissenbaum)
15年11月29日在6:24

#2 楼

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


#3 楼

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


评论


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

–丹·尼森鲍姆(Dan Nissenbaum)
15年11月29日在5:59



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

–丹·尼森鲍姆(Dan Nissenbaum)
15年11月29日在6:13



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

– Roelant
18年10月10日在18:44

#4 楼

您还可以查看underscore.js,它提供了诸如debounce之类的实用方法:

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


评论


在Lodash中也可以使用相同的方法-lodash.com/docs/#debounce

– Adarsh Madrecha
18年6月19日在6:59

#5 楼

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

#6 楼

使用标签延迟多功能调用

这是我使用的解决方案。它将延迟您想要的任何功能的执行。它可以是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
18年7月18日在12:33



#7 楼

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

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


评论


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

–user2662680
19年9月12日19:01在

#8 楼

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

function MyFunction() {

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

        //Execute the function code here...

    }, 500);
}


评论


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

– Vincent
1月14日22:17

#9 楼

此函数对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/

#10 楼

我很惊讶,没有人提到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中进行了测试。

评论


尝试过此操作,但在第一次键入时无法正常工作。

–雷恩·劳恩(LarsThorén)
2014-09-18 13:50

#11 楼

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

$('#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-您需要在执行setTimeout()之前在每次击键时使用clearTimeout()函数

–Picard
16-10-25在13:14

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

– Sagar Gala
16-10-25在17:44



#12 楼

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




#13 楼

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

#14 楼

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


支持多个输入,延迟可以同时运行。
忽略那些没有t更改了值(例如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可以按预期工作(如示例中一样)。
空值在th中被忽略。第二次验证。
小心排队,我希望它首先等待毫秒,但您可能希望切换参数以匹配setTimeout

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

#15 楼

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


#16 楼

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


#17 楼

使用

mytimeout = setTimeout( expression, timeout );


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

clearTimeout(mytimeout);


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

#18 楼

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


})


#19 楼

使用bindWithDelay jQuery插件:

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


#20 楼

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


#21 楼

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

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


#22 楼

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


#23 楼

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

#24 楼

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


#25 楼

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

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

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


#26 楼

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

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


#27 楼

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