ユーザーが入力を停止するまで.keyup()ハンドラーを遅らせる方法は?


643

検索フィールドがあります。現在、すべてのキーアップを検索します。したがって、誰かが「Windows」と入力すると、「W」、「Wi」、「Win」、「Wind」、「Windo」、「Window」、「Windows」のキーアップごとにAJAXで検索が行われます。

遅延を設定したいので、ユーザーが200ミリ秒入力を停止したときにのみ検索します。

keyup関数にはこれのオプションsetTimeoutはありません。私はを試しましたが、機能しませんでした。

どうやってやるの?



2
できれば、これを複製として閉じます。
a432511 2009

7
与えられ、受け入れられた答えが正しい限り、私は重複があることに害があるとは思いません。質問のデータベースに追加することは、何か良いことであり、努力する必要があります。
マティス

14
同じ質問が100問あると、将来の人々は皆が共有する素晴らしい回答から利益を得ることができないので、ベストプラクティスと修正を見つけるには、だまし絵を閉じて、全員を元の質問にリダイレクトする方が良いでしょう。重複が閉じられる理由の詳細については、stackoverflow.com / help / duplicatesを参照してください。
rncrtr 2013年

14
これは、おそらく複製していたものよりもはるかに人気があります。言葉遣いが良く、より良い答えがあり、グーグルで上位にランク付けされているなど。多くの人がこの答えから恩恵を受けています。振り返ってみると、これが閉じられていたら残念だったでしょう。重複として悪いいくつかのささいなものがありますが、これはそのカテゴリには分類されません。
ユーザーは

回答:


1124

私はこの小さな関数を同じ目的で使用します。ユーザーが指定した時間入力を停止した後、または次のような高速で発生するイベントで関数を実行します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プラグインをご覧ください


64
別の代替方法:github.com/bgrins/bindWithDelay/blob/master/bindWithDelay.js。それはあなたが説明したのとほとんど同じように機能します、私はそのパターンをたくさん使っているので、構文をより簡単にするためにjQueryプラグインとして実装しました。デモページは次のとおり
Brian Grinstead

2
入力した文字数を最初にハードコーディングしました。このアプローチは糖蜜(硫黄なし)のようにスムーズです
Har

2
これを$ .post()で使用すると、同時に入力されたすべてが送信されますが、キーアップした回数だけ送信されます。タイムアウトが経過したときにのみ送信する方法はありますか?
ntgCleaner 2012年

7
しかし、これは、個別の遅延を必要とする2つ以上の異なる関数を呼び出す場合には機能しません。代わりにこのソリューションを実行しました。stackoverflow.com/ a / 30503848/1834212
Miguel

4
一番上のコメントにリンクされている「bindWithDelay」プラグインをお勧めします。-しかし、私はこの質問(以下にHazeriderの答え@示唆stackoverflow.com/a/19259625/368896異なる要素ごとに異なるタイマーを許可するように結合この答えよりも良いと簡単な背後にある考え方を理解するために勉強する価値がある、スリック) -上記のプラグインコードで使用されているのと同じ原理ですが、理解しやすくなっています。
Dan Nissenbaum、2015年

60

タイプが完了した後に検索する場合は、グローバル変数を使用して、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);  
}   

39

CMSの回答に対するもう1つのわずかな強化。個別の遅延を簡単に許可するには、以下を使用できます。

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

1
私がこのコメントを書いている時点で600を超える賛成票を持つソリューションは、2つだけの賛成票(私のものを含む)を持つこの回答ほど良くありません。遅延を共有できる、または共有できない複数のウィジェットをサポートするため、明らかに優れています。すばらしい答えです。
Dan Nissenbaum、2015年

...回答の下のコメントのjQueryプラグインは、個別の要素の個別のタイマーも処理し、スロットルとイベントの引数も追加します(github.com/bgrins/bindWithDelay/blob/master/bindWithDelay.js)。そのプラグインの一般的なアプローチはコードと同じであるため、より洗練されたプラグインを理解する前に、コードを簡潔に理解するためにコードを注意深く検討する価値があります。ただし、このプラグインをお勧めしますが、スロットルやイベントデータの受け渡しが必要ない場合は、コードが最適です。2つの適切なオプション。ありがとう!
Dan Nissenbaum、2015年

1
うーん、それは私にとっては遅延しますが、someApiCallそれでも複数回トリガーされます-今は入力フィールドの最終値だけで。
Roelant


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)変数が入力と一致しません


12

ラベルを使用した多機能呼び出しの遅延

これは私が使用するソリューションです。それはあなたが望むどんな関数でも実行を遅らせます。それは、キーダウン検索クエリである可能性があります。おそらく前または次のボタンをすばやくクリックします(そうしないと、すばやく連続してクリックすると複数のリクエストが送信され、結局使用されません)。これは、各実行時間を格納するグローバルオブジェクトを使用し、それを最新のリクエストと比較します。

その結果、最後のクリック/アクションのみが実際に呼び出されます。これらのリクエストはキューに保存されているため、同じラベルのリクエストがキューに存在しない場合、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);});

同じ関数を呼び出しますが、ラベルが異なるため、独立して遅延します


私が取り組んでいるプロジェクトであなたのコードを使用していますが、問題があります。実際には何も遅延させません。あなたは援助を見て/提供したいと思うかもしれないと思った。 stackoverflow.com/questions/51393779/...
KDJ

10

誰かが同じ関数を遅らせたい場合、外部変数なしで次のスクリプトを使用できます。

function MyFunction() {

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

        //Execute the function code here...

    }, 500);
}

1
すごい!うまく機能する簡単なソリューション!
Fellipe Sanches

1
私はこれが好きです。正確に再利用できるわけではありませんが、簡単に理解できます。
Vincent

10

ユーザーがテキストフィールドへの入力を完了した後に関数を実行するように設計された超シンプルなアプローチ...

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

1
ありがとうございました!上記の一般的な回答は機能しませんでしたが、提案は完全に機能しました。
user2662680

1
2020年も引き続き機能
Romel Indemne

7

この関数は、要素を元に戻すために、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/


6

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

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

期待どおりに機能しない-フェッチ数は同じで、300ミリ秒遅れるだけ-setTimeout()を実行する前に、すべてのキーストロークでclearTimeout()関数を使用する必要があります
Picard

この場合、clearTimeoutを使用する必要はありません。setTimeout内の条件searchValue == $( '#searchText')。val()は、値が300ミリ秒前と同じかどうかを確認します。これが理にかなっている場合はお知らせください。ありがとう。
Sagar Gala

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


3

これは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、期待どおりに機能します(例のように)。
  • 空の値は2番目の検証で無視されます。
  • 気を付けろエンキュー、それは私がそれを好むが、あなたは試合にパラメータを切り替えたいことがあり、最初のミリ秒を期待しますsetTimeout

私が使用するソリューションは、別のレベルの複雑さを追加し、たとえば実行をキャンセルできるようにしますが、これは上に構築するための良いベースです。


3

CMSの回答Miguelの回答を組み合わせると、同時遅延を可能にする堅牢なソリューションが得られます。

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

異なるアクションを個別に遅延させる必要がある場合は、3番目の引数を使用します。

$('input.group1').keyup(function() {
    delay(function(){
        alert('Time elapsed!');
    }, 1000, 'firstAction');
});

$('input.group2').keyup(function() {
    delay(function(){
        alert('Time elapsed!');
    }, 1000, '2ndAction');
});

3

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

2

使用する

mytimeout = setTimeout( expression, timeout );

ここで、expressionは実行するスクリプトで、timeoutは実行前にミリ秒単位で待機する時間です。これはスクリプトに影響を与えませんが、タイムアウトが発生するまでその部分の実行を遅らせるだけです。

clearTimeout(mytimeout);

タイムアウトがリセット/クリアされるので、まだ実行されていない限り、スクリプトは式の中で(キャンセルのように)実行されません。


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


})


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

1

これが、フォームでの複数の入力を処理するために私が書いた提案です。

この関数は、入力フィールドのオブジェクトを取得し、コードに挿入します

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

1

ES6から、アロー関数構文も使用できます。

この例では、keyupユーザーが入力を完了した後searchFunc、クエリ要求を呼び出す前に、コードはイベントを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);
})

1

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


0

見てみましょうオートコンプリートのプラグインを。遅延または最小文字数を指定できることは知っています。プラグインを使用していなくても、コードを調べると、自分でプラグインを実装する方法についてのアイデアが得られます。


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

0

今日は少し遅れて見ましたが、他の誰かが必要になった場合に備えて、ここに置いておきます。関数を分離して再利用できるようにします。以下のコードは、stopと入力してから1/2秒待機します。

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

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


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);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.