关于jquery:当用户完成键入而不是键入时运行javascript函数?

Run javascript function when user finishes typing instead of on key up?

我想在用户输入文本框时触发ajax请求。 我不希望它在每次用户键入字母时都运行该函数,因为这会导致大量的ajax请求,但是我不希望它们也必须按下回车按钮。

有没有办法可以检测用户何时完成输入然后执行ajax请求?

在这里使用jQuery!
戴夫

  • 我想你需要为我们定义"完成打字"。
  • 虽然@Surreal Dreams的答案满足您的大部分要求,但如果用户在指定的超时后再次开始输入,则会向服务器发送多个请求。 请参阅下面的答案,该答案将每个XHR请求存储在一个变量中,并在启动一个新变量之前将其取消。 这实际上是Google在其即时搜索中所做的事情。
  • 可能重复jquery keyup延迟?
  • 选择的答案不正确有以下几个原因:1。即使用户打字,它也会在5秒后触发。 2.它不会等到用户按要求完成输入。 它触发了@Marko上面提到的多个请求。 请参阅下面的更正答案。
  • 模糊怎么样? 我猜当输入元素失去焦点时,用户肯定已完成输入。
  • 一个简单的谷歌搜索可以得到你简单的答案:schier.co/blog/2014/12/08/


所以,我猜猜完成打字就意味着你停了一会儿,说5秒钟。因此,考虑到这一点,让我们在用户释放密钥时启动计时器,并在按下密钥时清除它。我决定有问题的输入是#myInput。

做一些假设......

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//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
}

  • 谢谢:)我开始在问题上输入我的评论,并意识到我有一个不错的主意。
  • 很抱歉在@ss中感到很痛苦,但你在第二个事件上有两次keyup而不是keydown,而textbox不是一个有效的选择器,因为没有textbox元素:)
  • 你有keyup错字而不是keydown
  • 感谢关于拼写错误的笔记,我修复了它。我还将选择器更新为更真实的世界。
  • 此答案无法正常工作,除非用户输入速度非常慢,否则它将始终在5秒后触发。见下面的工作方案。
  • 如果你在这里遇到麻烦,因为计时器立即触发,试着在函数调用周围添加引号:setTimeout('functionToBeCalled',doneTypingInterval);
  • 将数据复制/粘贴到字段中时不起作用。
  • 不应使用字符串作为第一个参数调用@Largo setTimeout()。这样做相当于使用eval()。
  • @Macmee - 我继续并将你建议的编辑结合起来 - 我认为这是一个有效的改进。请随时添加任何进一步的评论。
  • 不应该是typingTimer = setTimeout(function() {doneTyping(); }, doneTypingInterval);只是好奇。上面提到的答案对我不起作用:/
  • 我看到一些注释,其中示例中的回调函数doneTyping立即运行。这通常是在函数名称后意外包含()的结果。请注意,它应该读取setTimeout(doneTyping,而不是setTimeout(doneTyping(),
  • @SurrealDreams嗨,亲爱的,我已经测试了你的问题的答案,但它没有用。如果你有时间可以帮我查一下我的问题吗?
  • 如果用户编写并按Tab键则不起作用
  • @Jessica使用粘贴你必须添加'paste'事件,如下所示:on('keyup paste',
  • @Sergey - 感谢评论,这非常有用。
  • 我废弃了单独的keydown函数,只是将它添加到第一个函数中。我还在那里添加了变化。所以它看起来像这个twitterUsernameField.on('keyup keydown change', function () {,它似乎工作正常...
  • 我可以在我的制作中使用它吗? :d
  • @colinrickels - 去吧。希望对你有帮助。
  • 以下是jsfiddle.net/matt_doran/g0rrj540/4上面的代码
  • 通过复制粘贴为我工作,我只是在这段代码var $input = $('#myInput');上更改了我的元素


上面选择的答案不起作用。

因为打字定时器被多次设置(在快速打字机等触发keydown之前按两次键盘)然后它没有正确清除。

下面的解决方案解决了这个问题,并在OP请求完成后调用X秒。它也不再需要冗余的keydown功能。我还添加了一个检查,以便在输入为空时不会发生函数调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//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
}

和vanilla JavaScript解决方案中的相同代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//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,但这里没有必要。只需用var替换let,用常规函数替换箭头函数。

  • 我相信你遇到的问题是你输入的速度足够快,你可以在释放第一个键之前按下第二个键。
  • 这不是他遇到的问题,这是任何用户可以复制的问题,就像我刚刚做的那样。想象一下,如果在超时结束时将ajax回发到服务器并使用响应执行某些操作。接受的答案会重复发回给我。发出警报并输入非常快。它不仅会在超时结束时触发,对我而言,它会反复触发。这是最完整的答案,它使用更少的代码,荣誉。
  • 好吧,即使输入为空,仍然可以调用该函数。如果要在输入再次为空时清除某些搜索结果(例如),该怎么办?
  • 我认为我们还应该考虑用户只是在字段内粘贴字符串的情况。
  • 如果您希望使用$('#myInput').on('input', function() {复制和粘贴,请使用$('#myInput').on('input', function() {。如果输入为空,我看不到检查点。假设他想要删除他输入的内容,这将无法进行ajax调用。
  • 为什么它是if ($('#myInput').val)而不是if ($('#myInput').val()).val应该是一个功能吗?
  • 为什么不使用$(this).val()?
  • @JoelFan或只是this.value,它非常接近你不需要额外的努力。
  • 按预期工作
  • 对我来说,它立即调用该函数:/不是在5秒之后,我尝试增加时间,但同样的事情发生了
  • @Xsmael,我认为直接将参数传递给setTimeout,如下所示:setTimeout(doneTyping(argument), doneTypingInterval);使函数运行两次。


它只有一行with underscore.js去抖功能:

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

这基本上是在我停止打字后500毫秒的doSomething。

欲了解更多信息:http://underscorejs.org/#debounce

  • 似乎也可用于jQuery:code.google.com/p/jquery-debounce和github.com/diaspora/jquery-debounce
  • 令人惊讶的是,人们如何吹捧"一行代码"至关重要。大。一行代码,需要加载1.1k JS文件。我不是在贬低这个因为是一个解决方案。但是一行大胆的东西让我感到烦恼,并导致膨胀代码。
  • @Wolfie,我同意你关于代码膨胀的问题,但是Underscore和Lodash是NPM中最受欢迎的两个实用工具,因此对于许多人来说这可能是一个单行解决方案。
  • @Paul我同意,如果你为其他目的加载库,那么它很好并且实际上有利于使用通用代码。但是,为了一行而加载K代码是没有效率的。特别是随着移动平台变得越来越不受限制的数据。许多欧元区也通过数据支付互联网费用。所以在合理的时候注意膨胀是很好的。
  • 为什么我得到:Uncaught ReferenceError: _ is not defined
  • @Wolfie他确实给了限定符,它是一行带下划线。
  • 你可以得到debouce()函数,npmjs.com/package/debounce,如果这就是你所需要的。效果很好。


是的,您可以在每个启动事件上设置2秒的超时,这将触发ajax请求。您还可以存储XHR方法并在随后的按键事件中将其中止,以便您节省带宽甚至更多。这是我为自己的自动完成脚本编写的内容。

1
2
3
4
5
6
7
8
9
10
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回调,它仍将使用请求锤击服务器。
  • Ixg,不,这不会。他使用的clearTimeout函数清除以前的Timeout事件,从而调用API调用。
  • @JohnSmith,我不同意它最好先清除定时器,然后检查xhr是否为空或者你的状况是什么,最后用ajax调用运行定时器


1
2
3
4
5
6
7
8
9
10
11
12
13
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/

  • 这对我有用,但要记住,当用户选中文本框时,你不会得到一个keyup事件,因为它已经失去了焦点。相反,将它设置为keydown似乎可以解决问题。


我喜欢Surreal Dream的答案,但我发现我的"doneTyping"功能会触发每个按键,即如果你真的很快输入"Hello";当你停止输入时,该功能只会发射一次,而不是只发射一次。

问题是javascript setTimeout函数似乎没有覆盖或杀死已设置的任何旧超时,但如果你自己这样做它就可以了!所以我只是在setTimeout之前添加了一个clearTimeout调用,如果设置了typingTimer。见下文:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//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答案的评论添加,但我是新用户并且没有足够的声誉。抱歉!


修改已接受的答案以处理其他案例,例如粘贴:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//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
}

前两个答案都不适合我。所以,这是我的解决方案:

1
2
3
4
5
6
7
8
9
var timeout = null;

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

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

好吧,严格来说不行,因为计算机无法猜到用户何时完成打字。当然,您可以在按键上启动计时器,并在每个后续按键上重置它。如果计时器到期,则用户没有键入计时器持续时间 - 您可以称之为"已完成输入"。

如果您希望用户在键入时暂停,则无法知道他们何时完成。

(当然,除非您完成后可以从数据中得知)


在这种情况下我不认为keyDown事件是必要的(请告诉我为什么我错了)。在我的(非jquery)脚本中,类似的解决方案看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)
    }

}

这是我对Stack Overflow的第一个回复,所以我希望有一天能帮助某人:)


声明以下delay函数:

1
2
3
4
5
6
7
var delay = (function () {
    var timer = 0;
    return function (callback, ms) {
        clearTimeout(timer);
        timer = setTimeout(callback, ms);
    };
})()

然后使用它:

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

  • 你的解决方案太棒了,兄弟!
  • @Developer非常感谢。我还添加了另一个答案来解决更复杂的场景(每页多个控件)。
  • 好的解决方案我使用这个而不是keydown我使用'输入'事件


我觉得使用input事件解决方案有点简单:

1
2
3
4
5
6
7
8
9
10
11
var typingTimer;
var doneTypingInterval = 500;

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

function doneTyping () {
    // code here
}

同意@going的答案。另一个对我有用的类似解决方案是下面的解决方案。唯一的区别是我使用.on("输入"......)而不是键盘。这仅捕获输入中的更改。其他键如Ctrl,Shift等将被忽略

1
2
3
4
5
6
7
8
9
10
11
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);
});

我正在我的列表中实现搜索,并且需要它是基于ajax的。这意味着在每次关键更改时,都应更新并显示搜索结果。这导致发送到服务器的这么多ajax调用,这不是一件好事。

经过一些工作,我做了一个方法来在用户停止输入时ping服务器。

这个解决方案对我有用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$(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

您会注意到在执行此操作时无需使用任何计时器。


我只想出一个简单的代码来等待用户完成输入:

步骤1.将超时设置为null,然后在用户键入时清除当前超时。

步骤2.触发keyup事件之前触发清除超时到变量define。

步骤3.对上面声明的变量定义超时;

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

javascript代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
};

  • 这个问题已经回答了......它与此非常相似


您可以使用onblur事件来检测文本框何时失去焦点:
https://developer.mozilla.org/en/DOM/element.onblur

这与"停止输入"不同,如果您关心用户输入一堆东西然后坐在那里仍然关注文本框的情况。

为此,我建议将setTimeout绑定到onclick事件,并假设在没有击键的x时间后,用户已停止输入。


这是我写的一个简单的JS代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!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>Submit after typing finished
<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;
    }
}


</head>
<body>

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

</body>
</html>

不确定我的需求是否有点奇怪,但我需要类似的东西,这就是我最终使用的东西:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$('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');
});

这允许我在我的应用程序中包含这样的元素:

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

当用户"完成输入"(2秒内没有动作)或转到另一个字段(模糊了元素)时会更新


一旦检测到文本框上的焦点,在按键上执行超时检查,并在每次触发时重置它。

超时完成后,执行ajax请求。


为什么不使用onfocusout?

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

如果它是一个表单,它们将始终保留每个输入字段的焦点,以便单击提交按钮,因此您知道没有输入将错过获取其onfocusout事件处理程序调用。

  • 当问题被提出时(近7年前),onfocusout支持不佳,并且onfocusout的效果也会有所不同。您必须等待用户将焦点留在元素上,而使用超时/去抖解决方案,一旦用户停止键入并且用户不需要切换焦点,它就会"触发"。用例示例将是这些注册表单之一,其中当用户停止输入潜在用户名时,在表单字段的右侧出现复选标记或"X",表示该名称可用。


每页多个计时器

所有其他答案仅适用于一个控件(包括我的其他答案)。
如果每页有多个控件(例如在购物车中),则只会调用用户输入内容的最后一个控件。在我的情况下,这肯定不是希望的行为 - 每个控件应该有自己的计时器。

要解决此问题,您只需将ID传递给函数并维护timeoutHandles字典,如下面的代码所示:

功能声明:

1
2
3
4
5
6
7
8
9
10
var delayUserInput = (function () {
    var timeoutHandles = {};    
    return function (id, callback, ms) {        
        if (timeoutHandles[id]) {
            clearTimeout(timeoutHandles[id]);
        }

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

功能用途:

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

如果您需要等到用户完成输入后使用简单:

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

使用ajax调用完成示例 - 这适用于我的寻呼机 - 每个列表的项目数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$(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);
            }
        });  
    });
});

如果您要查找特定长度(例如邮政编码字段):

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

  • 在发送ajax请求之前进行有限验证并不是一个坏主意。


哇,即使3条评论都是非常正确的!

  • 空输入不是跳过函数调用的原因,例如我在重定向之前从url中删除了废弃参数

  • .on ('input', function() { ... });应该用于触发keyuppastechange事件

  • 必须使用.val().value

  • 您可以使用$(this)内部事件函数而不是#id来处理多个输入

  • (我的决定)我使用匿名函数而不是setTimeout中的doneTyping来从n.4轻松访问$(this),但你需要先保存它,如var $currentInput = $(this);

    • 这是如何回答这个问题的?
    • @ThomasOrlita这个答案补充了3个评分最高的答案。看看接受的一个:1。它不支持paste,它不使用this等(我认为这很重要,但我不想迷失在大量的评论中)
    • 我想你应该把它写成答案下的评论......