【问题标题】:How to delay the .keyup() handler until the user stops typing?如何延迟 .keyup() 处理程序直到用户停止输入?
【发布时间】:2010-12-26 22:29:17
【问题描述】:

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

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

keyup函数中没有这个选项,我试过setTimeout,但是没用。

我该怎么做?

【问题讨论】:

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

标签: javascript jquery


【解决方案1】:

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

【讨论】:

    【解决方案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】:

      使用

      mytimeout = setTimeout( expression, timeout );
      

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

      clearTimeout(mytimeout);
      

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

      【讨论】:

        【解决方案4】:

        我将这个小函数用于相同的目的,在用户停止输入指定的时间或在高频率触发的事件(如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 年 5 月 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 插件。

        【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          【解决方案6】:

          使用bindWithDelay jQuery 插件:

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

          【讨论】:

            【解决方案7】:

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

            【讨论】:

              【解决方案8】:

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

              【讨论】:

                【解决方案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 中测试。

                  【讨论】:

                  • 已经尝试过了,但在第一次按键时无法正常工作。
                  【解决方案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,因此它们必须定义且唯一,或者您可以根据自己的情况进行调整。
                  • delay 返回一个易于扩展以满足您自己需求的对象。
                  • 用于延迟的原始元素绑定到回调,因此this 可以按预期工作(如示例中所示)。
                  • 在第二次验证中忽略空值。
                  • 注意enqueue,它首先需要毫秒,我更喜欢这样,但您可能需要切换参数以匹配setTimeout

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

                  【讨论】:

                    【解决方案12】:

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

                    【讨论】:

                      【解决方案13】:

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

                      $('#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 毫秒前的值相同。让我知道这是否有意义。谢谢。
                      【解决方案14】:

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

                      【讨论】:

                        【解决方案15】:
                        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
                        };
                        

                        【讨论】:

                          【解决方案16】:

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

                          【讨论】:

                            【解决方案17】:

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

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

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

                            【讨论】:

                              【解决方案18】:

                              使用标签延迟多功能调用

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

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

                              【讨论】:

                                【解决方案19】:

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

                                function MyFunction() {
                                
                                    //Delaying the function execute
                                    if (this.timer) {
                                        window.clearTimeout(this.timer);
                                    }
                                    this.timer = window.setTimeout(function() {
                                
                                        //Execute the function code here...
                                
                                    }, 500);
                                }
                                

                                【讨论】:

                                  【解决方案20】:

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

                                  【讨论】:

                                    【解决方案21】:

                                    说明

                                    使用变量来存储超时函数。然后使用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>

                                    【讨论】:

                                      【解决方案22】:

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

                                          var timeOutVar
                                      $(selector).on('keyup', function() {
                                      
                                                          clearTimeout(timeOutVar);
                                                          timeOutVar= setTimeout(function(){ console.log("Hello"); }, 500);
                                                      });
                                      

                                      【讨论】:

                                        【解决方案23】:

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

                                        【讨论】:

                                          【解决方案24】:

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

                                          更新的 Typescript 版本:

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

                                          【讨论】:

                                            【解决方案25】:

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

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

                                            【讨论】:

                                              【解决方案26】:
                                              // 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);
                                              }
                                              

                                              【讨论】:

                                                【解决方案27】:

                                                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);
                                                });
                                                &lt;input type="text" id="input" placeholder="Type here..."/&gt;

                                                【讨论】:

                                                  【解决方案28】:

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

                                                  【讨论】:

                                                    猜你喜欢
                                                    • 2018-09-17
                                                    • 2019-11-03
                                                    • 2019-06-15
                                                    • 1970-01-01
                                                    • 1970-01-01
                                                    相关资源
                                                    最近更新 更多