ユーザーがキーを押す代わりに入力を終了したときにJavaScript関数を実行しますか?


463

ユーザーがテキストボックスへの入力を完了したら、ajaxリクエストをトリガーしたいのですが。ユーザーが文字を入力するたびにこの関数が実行されるのは望ましくありません。これにより、ajaxリクエストが大量に発生するためです。ただし、Enterボタンを押す必要もありません。

ユーザーが入力を完了したことを検出してajaxリクエストを行う方法はありますか?

ここでjQueryを使用します!デイブ


21
私たちのために「タイピングの仕上げ」を定義する必要があると思います。
シュールドリームズ

8
@Surreal Dreamsの回答はほとんどの要件を満たしていますが、ユーザーが指定されたタイムアウト後に再度入力を開始すると、複数のリクエストがサーバーに送信されます。各XHRリクエストを変数に格納し、新しいリクエストを開始する前にそれをキャンセルする以下の私の回答を参照してください。これは実際にGoogleがインスタント検索で行うことです。
マルコ


1
ぼかしはどうですか?入力要素がフォーカスを失ったとき、ユーザーは間違いなく入力を終えたと思います。
Don P

3
:シンプルなGoogle検索を使用すると、単純な答え得ている可能性がありschier.co/blog/2014/12/08/...
Cannicide

回答:


689

つまり、タイピングを終了するということは、しばらくの間、たとえば5秒間停止することを意味します。それを念頭に置いて、ユーザーがキーを離したときにタイマーを開始し、キーを押したときにそれをクリアしてみましょう。問題の入力は#myInputになると判断しました。

いくつかの仮定をしています...

//setup before functions
var typingTimer;                //timer identifier
var doneTypingInterval = 5000;  //time in ms, 5 second for example
var $input = $('#myInput');

//on keyup, start the countdown
$input.on('keyup', function () {
  clearTimeout(typingTimer);
  typingTimer = setTimeout(doneTyping, doneTypingInterval);
});

//on keydown, clear the countdown 
$input.on('keydown', function () {
  clearTimeout(typingTimer);
});

//user is "finished typing," do something
function doneTyping () {
  //do something
}

4
ありがとう:)私は質問にコメントを入力し始めて、私はまともな考えを持っていることに気付きました。
Surreal Dreams

26
この回答は正しく機能しません。ユーザーが非常にゆっくりと入力しない限り、常に5秒後に起動します。以下の解決策を参照してください。
行く

3
ここで問題が発生した場合は、タイマーがすぐに発生するため、関数呼び出しを引用符で囲んでみてください。setTimeout( 'functionToBeCalled'、doneTypingInterval);
ラルゴ

3
doneTyping例では、コールバック関数がすぐに実行されているコメントがいくつか表示されます。これは通常()、関数名の後に誤って含めた結果です。読むべきではsetTimeout(doneTyping,ないことに注意してくださいsetTimeout(doneTyping(),
Anthony DiSanti

2
@Jessicaでペーストを使用するには、次のような「ペースト」イベントを追加する必要があります:on( 'keyup paste'、
Sergey

397

上記の選択した回答は機能しません。

typingTimerは時々複数回設定されるため(高速タイパーでキーダウンがトリガーされる前にキーアップが2回押されるなど)、適切にクリアされません。

以下のソリューションはこの問題を解決し、OPが要求したように、終了してからX秒後に呼び出します。また、冗長なキーダウン機能も不要になりました。入力が空の場合に関数呼び出しが発生しないように、チェックも追加しました。

//setup before functions
var typingTimer;                //timer identifier
var doneTypingInterval = 5000;  //time in ms (5 seconds)

//on keyup, start the countdown
$('#myInput').keyup(function(){
    clearTimeout(typingTimer);
    if ($('#myInput').val()) {
        typingTimer = setTimeout(doneTyping, doneTypingInterval);
    }
});

//user is "finished typing," do something
function doneTyping () {
    //do something
}

そして、バニラJavaScriptソリューションの同じコード:

//setup before functions
let typingTimer;                //timer identifier
let doneTypingInterval = 5000;  //time in ms (5 seconds)
let myInput = document.getElementById('myInput');

//on keyup, start the countdown
myInput.addEventListener('keyup', () => {
    clearTimeout(typingTimer);
    if (myInput.value) {
        typingTimer = setTimeout(doneTyping, doneTypingInterval);
    }
});

//user is "finished typing," do something
function doneTyping () {
    //do something
}

このソリューションはES6を使用しますが、ここでは必要ありません。ただ、交換するletvar、定期的な機能を備えた矢印機能。


5
まあ、入力が空の場合でも関数を呼び出すと便利でしょう。入力が再び空白になったときに、(たとえば)一部の検索結果をクリアしたい場合はどうなりますか?
rvighne 2014

7
ユーザーがフィールド内に来た文字列を単に貼り付ける条件も考慮する必要があると思います。
Vishal Nair 2014

10
$('#myInput').on('input', function() {これをコピーして貼り付ける場合に使用します。入力が空かどうかのチェックポイントがわかりません。彼がタイプしたものを消去したい場合、これはajax呼び出しを行うことに失敗します。
ビリーノア2014年

3
なぜそれのif ($('#myInput').val)代わりにif ($('#myInput').val())ですか?.val関数にする必要がありますか?
wlnirvana

4
$(this).val()を使用しないのはなぜですか?
JoelFan 2015

76

これは、underscore.jsデバウンス関数を含む1行にすぎません。

$('#my-input-box').keyup(_.debounce(doSomething , 500));

これは基本的に、入力を停止してから500ミリ秒後にdoSomethingと表示されます。

詳細:http : //underscorejs.org/#debounce


5
jQueryでも利用できるようです:code.google.com/p/jquery-debounceおよびgithub.com/diaspora/jquery-debounce
koppor

63
「ONE LINE OF CODE」が非常に重要であると人々がいかに宣伝しているのかに驚かされます。すごい。ロードに1.1k JSファイルを必要とする1行のコード。が解決策であるため、私はこれに反対票を投じていません。しかし、1行の大胆なことは私をいらだたせるだけでなく、肥大化したコードにつながります。
Wolfie、

1
@Wolfie、私はコードの膨張についてあなたに同意しますが、UnderscoreとLodashはNPMの依存型ユーティリティのトップ2であるため、これは多くの人にとって1行のソリューションになる可能性があります。
ポール

3
@Paul私は、ライブラリを他の目的でロードする場合、一般的なコードを使用することが実際に有益であることに同意します。しかし、1行の目的でKを超えるコードをロードするのは効率的ではありません。特に、モバイルプラットフォームがますます無制限のデータになりました。そして、多くのユーロ圏もデータによってインターネットに支払います。したがって、そうすることが合理的である場合、膨満に注意を払うのは良いことです。
Wolfie

なぜ私が得たのか:Uncaught ReferenceError: _ is not defined
Wilf

44

はい、Ajaxリクエストを発生させるキーアップイベントごとに、たとえば2秒のタイムアウトを設定できます。XHRメソッドを保存し、後続のキープレスイベントで中止して、帯域幅をさらに節約することもできます。これが私のオートコンプリートスクリプト用に私が書いたものです。

var timer;
var x;

$(".some-input").keyup(function () {
    if (x) { x.abort() } // If there is an existing XHR, abort it.
    clearTimeout(timer); // Clear the timer so we don't end up with dupes.
    timer = setTimeout(function() { // assign timer a new timeout 
        x = $.getJSON(...); // run ajax request and store in x variable (so we can cancel)
    }, 2000); // 2000ms delay, tweak for faster/slower
});

お役に立てれば、

マルコ


これはお勧めしません。コードは、キーが押されるたびに APIリクエストをトリガーします。次に、別のキーが押された場合、要求をキャンセルします。このソリューションでは、ユーザーが入力している間はAJAXコールバックがトリガーされませんが、サーバーはリクエストで攻撃されます。
lxg 2017年

Ixg、いいえ、これはしません。彼が使用するclearTimeout関数は、以前のTimeoutイベントをクリアして、API呼び出しを呼び出します。
ジョンスミス

@JohnSmith、私はおそらく最初にタイマーをクリアし、次にxhrが空であるかどうか、または状況がどのようなものかを確認し、最後にajax呼び出しでタイマーを実行する方が良いとは思わない
Fleuv

20
var timer;
var timeout = 1000;

$('#in').keyup(function(){
    clearTimeout(timer);
    if ($('#in').val) {
        timer = setTimeout(function(){
            //do stuff here e.g ajax call etc....
             var v = $("#in").val();
             $("#out").html(v);
        }, timeout);
    }
});

ここに完全な例:http : //jsfiddle.net/ZYXp4/8/


これは私にとってはうまくいきましたが、ユーザーがテキストボックスからタブアウトすると、その時点までにフォーカスを失っているため、キーアップイベントを取得できないことに注意してください。代わりにkeydownに設定すると、問題が解決するようです。
horatius83 2013

12

上位2つの回答はどちらも機能しません。だから、ここに私の解決策があります:

var timeout = null;

$('#myInput').keyup(function() {
    clearTimeout(timeout);

    timeout = setTimeout(function() {
        //do stuff here
    }, 500);
});

11

私はSurreal Dreamの答えが好きですが、私の「doneTyping」関数はキーを押すたびに起動することがわかりました。入力を停止したときに1回だけ起動するのではなく、関数は5回起動します。

問題は、javascriptのsetTimeout関数が、設定されている古いタイムアウトを上書きまたは強制終了しないように見えることですが、自分で実行すると機能します。そのため、typingTimerが設定されている場合は、setTimeoutの直前にclearTimeout呼び出しを追加しました。下記参照:

//setup before functions
var typingTimer;                //timer identifier
var doneTypingInterval = 5000;  //time in ms, 5 second for example

//on keyup, start the countdown
$('#myInput').on("keyup", function(){
    if (typingTimer) clearTimeout(typingTimer);                 // Clear if already set     
    typingTimer = setTimeout(doneTyping, doneTypingInterval);
});

//on keydown, clear the countdown 
$('#myInput').on("keydown", function(){
    clearTimeout(typingTimer);
});

//user is "finished typing," do something
function doneTyping () {
    //do something
}

注意:これをSurreal Dreamの回答へのコメントとして追加したかったのですが、私は新しいユーザーであり、十分な評判がありません。ごめんなさい!


10

貼り付けなどの追加のケースを処理するために、受け入れられた回答を変更します。

//setup before functions
var typingTimer;                //timer identifier
var doneTypingInterval = 2000;  //time in ms, 2 second for example
var $input = $('#myInput');

// updated events 
$input.on('input propertychange paste', function () {
    clearTimeout(typingTimer);
    typingTimer = setTimeout(doneTyping, doneTypingInterval);      
});

//user is "finished typing," do something
function doneTyping () {
  //do something
}

6

この場合、keyDownイベントは必要ないと思います(間違っている場合は理由を教えてください)。私の(jquery以外の)スクリプトでは、同様のソリューションは次のようになります。

var _timer, _timeOut = 2000; 



function _onKeyUp(e) {
    clearTimeout(_timer);
    if (e.keyCode == 13) {      // close on ENTER key
        _onCloseClick();
    } else {                    // send xhr requests
        _timer = window.setTimeout(function() {
            _onInputChange();
        }, _timeOut)
    }

}

スタックオーバーフローに対する私の最初の返信です。いつか誰かの役に立つことを願っています:)


6

次のdelay関数を宣言します。

var delay = (function () {
    var timer = 0;
    return function (callback, ms) {
        clearTimeout(timer);
        timer = setTimeout(callback, ms);
    };
})()

そしてそれを使います:

let $filter = $('#item-filter');
$filter.on('keydown', function () {
    delay(function () {            
        console.log('this will hit, once user has not typed for 1 second');            
    }, 1000);
});    

あなたの解決策は素晴らしいです、仲間!
開発者

1
@Developerありがとうございます。また、もう1つの回答を追加して、もう少し複雑なシナリオ(ページごとに複数のコントロール)を解決しました。
Fabian Bigler、

良い解決策。イムこれを使用して代わりのkeydownイムの「入力」イベント使用して
Budyn

キーアップイベントで、それは私にとってはうまくいきました。ありがとう。
シナンエルデム

6

遅い回答ですが、2019年であり、これはかなりのES6を使用して完全に達成可能であり、サードパーティのライブラリは使用できないため、追加します。高評価の回答のほとんどはかさばり、変数が多すぎて圧迫されます。

この優れたブログ投稿から抜粋したエレガントなソリューション

function debounce(callback, wait) {
  let timeout;
  return (...args) => {
      const context = this;
      clearTimeout(timeout);
      timeout = setTimeout(() => callback.apply(context, args), wait);
  };
}

window.addEventListener('keyup', debounce( () => {
    // code you would like to run 1000ms after the keyup event has stopped firing
    // further keyup events reset the timer, as expected
}, 1000))

1
これは本当に今年与えられたトップの答えでなければなりません
ダークカバ

4

まあ、厳密にいえば、ユーザーが入力を終えたときにコンピューターが推測できないためです。もちろん、キーアップ時にタイマーを起動し、その後のキーアップごとにタイマーをリセットすることができます。タイマーの期限が切れた場合、ユーザーはタイマーの時間を入力していません。「終了した入力」と呼ぶことができます。

ユーザーが入力中に一時停止することを期待する場合、いつ完了したかを知る方法はありません。

(もちろん、データがいつ完了したかを知ることができる場合を除きます)


3

inputイベントの方がソリューションは少しシンプルだと思います:

var typingTimer;
var doneTypingInterval = 500;

$("#myInput").on("input", function () {
    window.clearTimeout(typingTimer);
    typingTimer = window.setTimeout(doneTyping, doneTypingInterval);
});

function doneTyping () {
    // code here
}

3

@goingの回答に同意します。私のために働いた別の同様の解決策は以下の解決策です。唯一の違いは、キーアップの代わりに.on( "input" ...)を使用していることです。これは、入力の変更のみをキャプチャします。Ctrl、Shiftなどの他のキーは無視されます

var typingTimer;                //timer identifier
var doneTypingInterval = 5000;  //time in ms (5 seconds)

//on input change, start the countdown

$('#myInput').on("input", function() {    
    clearTimeout(typingTimer);
    typingTimer = setTimeout(function(){
        // doSomething...
    }, doneTypingInterval);
});

3

ユーザーが入力を完了するのを待つ簡単なコードを見つけました。

ステップ1.タイムアウトをnullに設定し、ユーザーが入力しているときに現在のタイムアウトをクリアします。

手順2. keyupイベントがトリガーされる前に、タイムアウトを変数defineにクリアします。

ステップ3.上で宣言した変数にタイムアウトを定義します。

<input type="text" id="input" placeholder="please type" style="padding-left:20px;"/>
<div class="data"></div>

JavaScriptコード

var textInput = document.getElementById('input');
var textdata = document.querySelector('.data');
// Init a timeout variable to be used below
var timefired = null;

// Listen for keystroke events
// Init a timeout variable to be used below
var timefired = null;// Listen for keystroke events
textInput.onkeyup = function (event) {
clearTimeout(timefired);
timefired = setTimeout(function () {
    textdata.innerHTML = 'Input Value:'+ textInput.value;
  }, 600);
};

1
質問はすでに答えられています...そしてそれはこれにかなり似ています
Mixone

3

私は自分のリストで検索を実装していて、それをajaxベースにする必要がありました。つまり、キーを変更するたびに、検索結果を更新して表示する必要があります。これにより、サーバーに送信されるajax呼び出しが非常に多くなり、これは良いことではありません。

いくつかの作業を行った後、ユーザーが入力をやめたときにサーバーにpingを送信する方法を作りました。

この解決策は私にとってうまくいきました:

$(document).ready(function() {
    $('#yourtextfield').keyup(function() {
        s = $('#yourtextfield').val();
        setTimeout(function() { 
            if($('#yourtextfield').val() == s){ // Check the value searched is the latest one or not. This will help in making the ajax call work when client stops writing.
                $.ajax({
                    type: "POST",
                    url: "yoururl",
                    data: 'search=' + s,
                    cache: false,
                    beforeSend: function() {
                       // loading image
                    },
                    success: function(data) {
                        // Your response will come here
                    }
                })
            }
        }, 1000); // 1 sec delay to check.
    }); // End of  keyup function
}); // End of document.ready

これを実装している間、タイマーを使用する必要がないことがわかります。


2

これは私が書いた簡単なJSコードです:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pt-br" lang="pt-br">
<head><title>Submit after typing finished</title>
<script language="javascript" type="text/javascript">
function DelayedSubmission() {
    var date = new Date();
    initial_time = date.getTime();
    if (typeof setInverval_Variable == 'undefined') {
            setInverval_Variable = setInterval(DelayedSubmission_Check, 50);
    } 
}
function DelayedSubmission_Check() {
    var date = new Date();
    check_time = date.getTime();
    var limit_ms=check_time-initial_time;
    if (limit_ms > 800) { //Change value in milliseconds
        alert("insert your function"); //Insert your function
        clearInterval(setInverval_Variable);
        delete setInverval_Variable;
    }
}

</script>
</head>
<body>

<input type="search" onkeyup="DelayedSubmission()" id="field_id" style="WIDTH: 100px; HEIGHT: 25px;" />

</body>
</html>

1

onblurイベントを使用して、テキストボックスがフォーカスを失ったことを検出できます:https : //developer.mozilla.org/en/DOM/element.onblur

これは、ユーザーがたくさんのものを入力してから、テキストボックスにフォーカスしたままそこに座っている場合に気を付ければ、「タイピングを停止する」と同じではありません。

そのため、setTimeoutをonclickイベントに関連付け、キーストロークがない状態でx時間経過した後、ユーザーが入力を停止したと想定します。


1

なぜonfocusoutを使用しないのですか?

https://www.w3schools.com/jsreF/event_onfocusout.asp

フォームの場合は、送信ボタンをクリックするために常にすべての入力フィールドにフォーカスを置いたままになるので、onfocusoutイベントハンドラーが呼び出されても入力を見逃すことはありません。


1
質問されたとき(ほぼ7年前)にonfocusoutのサポートは不十分でした。また、onfocusoutによる効果も多少異なります。ユーザーが要素にフォーカスを置くのを待つ必要がありますが、タイムアウト/デバウンスソリューションでは、ユーザーが入力を停止するとすぐに「発火」し、ユーザーはフォーカスを切り替える必要がありません。ユースケースの例は、ユーザーが潜在的なユーザー名の入力を停止すると、フォームフィールドの右側にチェックマークまたは「X」が表示され、名前が利用可能であることを示す登録フォームの1つです。
David Zorychta 2017

検索バーがフォーカスされている場合、誰も外に出る必要はありません。しばらくすると、何も入力していないため結果が直接表示されます
P Satish Patro

1

ユーザーがフィールドから離れる必要がある場合は、JavaScriptのOnchangeの代わりに「onBlur」を使用できます。

  <TextField id="outlined-basic"  variant="outlined" defaultValue={CardValue} onBlur={cardTitleFn} />

それが必要ない場合は、タイマーを設定することをお勧めします。


0

テキストボックスにフォーカスを検出したら、キーを押したときにタイムアウトチェックを実行し、トリガーされるたびにリセットします。

タイムアウトが完了したら、ajaxリクエストを実行します。


0

特定の長さ(郵便番号フィールドなど)を探している場合:

$("input").live("keyup", function( event ){
if(this.value.length == this.getAttribute('maxlength')) {
        //make ajax request here after.
    }
  });

ajaxリクエストを送信する前に制限付き検証を行うことは悪い考えではありません。
2015年

0

私のニーズが奇妙なものであるかどうかはわかりませんが、私はこれに似たものが必要でした、そしてこれが私が最終的に使用したものです:

$('input.update').bind('sync', function() {
    clearTimeout($(this).data('timer'));            
    $.post($(this).attr('data-url'), {value: $(this).val()}, function(x) {
        if(x.success != true) {
            triggerError(x.message);    
        }
    }, 'json');
}).keyup(function() {
    clearTimeout($(this).data('timer'));
    var val = $.trim($(this).val());
    if(val) {
        var $this = $(this);
        var timer = setTimeout(function() {
            $this.trigger('sync');
        }, 2000);
        $(this).data('timer', timer);
    }
}).blur(function() {
    clearTimeout($(this).data('timer'));     
    $(this).trigger('sync');
});

これにより、アプリケーションに次のような要素を含めることができます。

<input type="text" data-url="/controller/action/" class="update">

これは、ユーザーが「入力を完了」したとき(2秒間何もしない)または別のフィールドに移動したとき(要素からぼかされる)に更新されます。


0

ユーザーが入力を完了するまで待つ必要がある場合は、次のように使用します。

$(document).on('change','#PageSize', function () {
    //Do something after new value in #PageSize       
});

ajax呼び出しを使用した完全な例-これは私のポケットベルで機能します-リストごとの項目数:

$(document).ready(function () {
    $(document).on('change','#PageSize', function (e) {
        e.preventDefault();
        var page = 1;
        var pagesize = $("#PageSize").val();
        var q = $("#q").val();
        $.ajax({
            url: '@Url.Action("IndexAjax", "Materials", new { Area = "TenantManage" })',
            data: { q: q, pagesize: pagesize, page: page },
            type: 'post',
            datatype: "json",
            success: function (data) {
                $('#tablecontainer').html(data);
               // toastr.success('Pager has been changed', "Success!");
            },
            error: function (jqXHR, exception) {
                ShowErrorMessage(jqXHR, exception);
            }
        });  
    });
});    

0

ページごとに複数のタイマー

他のすべての回答は、1つのコントロールに対してのみ機能します(他の回答も含まれています)。ページごとに複数のコントロールがある場合(ショッピングカートなど)、ユーザーが何かを入力した最後のコントロールのみが呼び出されます。私の場合、これは確かに望ましい動作ではありません-各コントロールには独自のタイマーが必要です。

これを解決するには、次のコードのように、IDを関数に渡し、timeoutHandles辞書を維持するだけです。

関数宣言:

var delayUserInput = (function () {
    var timeoutHandles = {};    
    return function (id, callback, ms) {        
        if (timeoutHandles[id]) {
            clearTimeout(timeoutHandles[id]);
        }

        timeoutHandles[id] = setTimeout(callback, ms);             
    };
})();

関数の使用:

  delayUserInput('yourID', function () {
     //do some stuff
  }, 1000);

0

シンプルでわかりやすい。

var mySearchTimeout;
$('#ctl00_mainContent_CaseSearch').keyup(function () {
   clearTimeout(mySearchTimeout);
   var filter = $(this).val();
   mySearchTimeout = setTimeout(function () { myAjaxCall(filter); }, 700);
   return true;
});

0

ES6構文とともに関数にパラメーターを渡すため。

$(document).ready(() => {
    let timer = null;
     $('.classSelector').keydown(() => {
     clearTimeout(timer); 
     timer = setTimeout(() => foo('params'), 500);
  });
});

const foo = (params) => {
  console.log(`In foo ${params}`);
}

-2

わあ、3件のコメントでもかなり正しいです。

  1. 空の入力は、関数呼び出しをスキップする理由ではありません。たとえば、リダイレクトの前にURLから無駄なパラメーターを削除します

  2. .on ('input', function() { ... });トリガーに使用すべきであるkeyuppastechangeイベント

  3. 間違いなく.val()または.value使用する必要があります

  4. 複数の入力を処理する$(this)代わりに、内部イベント関数を使用できます#id

  5. (私の決定)n.4 から簡単にアクセスするにはdoneTyping、in ではなく匿名関数を使用しますが、最初に次のように保存する必要があります。setTimeout$(this)var $currentInput = $(this);

編集一部の人々は、準備ができたコードをコピーして貼り付ける可能性がないと方向性を理解しないことがわかります。ほら

var typingTimer;
//                  2
$("#myinput").on('input', function () {
    //             4     3
    var input = $(this).val();
    clearTimeout(typingTimer);
    //                           5
    typingTimer = setTimeout(function() {
        // do something with input
        alert(input);
    }, 5000);      
});

これはどのように質問に答えますか?
Thomas Orlita 2017年

@ThomasOrlitaこの回答は3つの最も評価された回答を補足します。受け入れられたものを見てください。1.サポートpasteしていない、使用していないthis、など(重要だと思いますが、大量のコメントで迷子になりたくありません)
vladkras

1
これは質問の答えにはなりません。別の回答についてコメントしたい場合は、コメントシステムがあります(私は今それを使用しています!)
GrayedFox
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.