运行javascript函数时,用户完成输入,而不是上键?

当用户在文本框中输入完成时,我想触发一个ajax请求。我不希望它在每次用户键入一封信时运行该函数,因为这会导致大量的ajax请求,但我也不希望他们不得不按回车键。

是否有一种方法,让我可以检测当用户已经完成输入,然后做ajax请求?

在这里使用jQuery !

423580 次浏览

嗯,严格来说不是,因为计算机无法猜测用户何时完成输入。当然,你可以在按下键时触发计时器,并在每次按下键时重置它。如果计时器过期,用户在计时器持续时间内还没有输入-你可以称之为“完成输入”。

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

(当然,除非你能从数据中判断他们何时完成)

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

当超时结束时,执行ajax请求。

你可以使用onblur事件来检测文本框何时失去焦点: # EYZ0 < / p >

这与“停止输入”不一样,如果你关心用户输入了一堆东西,然后坐在那里,文本框仍然聚焦。

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

所以,我猜完成打字的意思是你停下来一段时间,比如5秒钟。考虑到这一点,让我们在用户释放一个键时启动一个计时器,并在他们按下一个键时清除它。我决定输入问题将是#myInput。

做一些假设…

//setup before functions
var typingTimer;                //timer identifier
var doneTypingInterval = 5000;  //time in ms, 5 seconds 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
}

是的,你可以在每个key up事件上设置一个超时,比如2秒,它会触发一个ajax请求。您还可以存储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
});

希望这能有所帮助,

Marko

上面选择的答案不成立。

因为typingTimer偶尔会设置多次(对于快速键入者,按下键之前按下键两次),那么它就不能正确清除。

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

//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,但在这里没有必要。只需将let替换为var,将箭头函数替换为常规函数。

我不认为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)
}


}

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

如果您正在寻找特定的长度(例如邮政编码字段):

$("input").live("keyup", function( event ){
if(this.value.length == this.getAttribute('maxlength')) {
//make ajax request here after.
}
});
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/

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

$('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秒内没有动作)或转到另一个字段(模糊出元素)时更新

它只是一行underscore.js debounce函数:

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

这基本上是在我停止输入后500毫秒显示doSomething

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

我在我的清单中实现搜索,需要它是基于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

您将注意到,在实现此操作时不需要使用任何计时器。

我喜欢Surreal Dream的答案,但我发现我的“doneTyping”函数会在每次按键时触发,即如果你快速输入“Hello”;当您停止输入时,该函数将触发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的回答的评论,但我是一个新用户,没有足够的声誉。对不起!

这是我写的一个简单的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>

哇,甚至有3条评论都是正确的!

  1. 空输入不是跳过函数调用的原因,例如,我在重定向之前从url中删除浪费参数

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

  3. 必须使用.val().value

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

  5. (我的决定)我在setTimeout中使用匿名函数而不是doneTyping,以便从n.4轻松访问$(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);
});

如果你需要等待,直到用户完成输入使用简单的这个:

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

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

var typingTimer;
var doneTypingInterval = 500;


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


function doneTyping () {
// code here
}

修改接受的答案以处理额外的情况,如paste:

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

同意@going的答案。下面是另一个类似的解决方案。唯一的区别是我使用的是.on("input"…)而不是keyup。这只捕获输入中的更改。其他按键如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);
});

为什么不直接使用onfocusout?

# EYZ0

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

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

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

步骤2。触发keyup事件之前定义的变量的清除超时。

步骤3.为上面声明的变量定义timeout;

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

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

var timeout = null;


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


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

声明下面的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);
});

每个页面有多个计时器

所有其他答案只适用于一个控制 (我的另一个答案包括在内)。 如果每个页面有多个控件(例如在购物车中)在我的例子中,这当然不是希望的行为-每个控件都应该有自己的计时器

要解决这个问题,你只需要向函数传递一个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);

很晚的答案,但我添加它是因为现在是2019年,这完全可以用漂亮的ES6实现,没有第三方库,而且我发现大多数高评分的答案都很笨重,有太多变量。

优雅的解决方案从这篇精彩的博客文章。

function debounce(callback, wait) {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(function () { callback.apply(this, 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))

简单易懂。

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

用于将参数与ES6语法一起传递给函数。

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


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

如果有必要让用户离开字段,我们可以使用“onBlur”而不是Javascript中的Onchange

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

如果这是不必要的设置定时器将是一个很好的选择。

不是一个直接的答案,但如果有人在寻找AngularJS的解决方案。我根据这里流行的解决方案写了一个指令。

 app.directive("ngTypeEnds", ["$timeout", function ($timeout) {
return function (scope, element, attrs) {
var typingTimer;
element.bind("keyup", function (event) {
if (typingTimer)
$timeout.cancel(typingTimer);
if (angular.element(element)[0].value) {
typingTimer = $timeout(function () {
scope.$apply(function () {
scope.$eval(attrs.ngTypeEnds);
});
}, 500);
}
event.preventDefault();
});
};
}]);

你们听说过javascript中的闭包吗?!

它非常简单直接,只需将当前输入值与setTimeOut函数关闭的旧值进行比较,瞧,你就完成了。

let timer;
$('#myInput').on('keyup', function() {
window.clearTimeout(timer);
// here is the closures javascript magic happens.
const value = $(this).val();
timer = setTimeout(() => {
if(value === $(this).val() && $(this).val()!== ''){
alert($(this).val());
}
}, 500);
})

我需要为特定的控制运行我的程序,这对我来说很有效:

function debounce(func, timeout) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); }, timeout);
};
}


$('#txtFilterClientCode').keyup(debounce(function () {
console.log("Test");
}, 1000));

下面是一个解决方案,它在不输入1秒后触发,但在输入为空白时也会立即触发。这在用户删除输入查询后清除搜索结果时非常有用。此解决方案还支持复制和粘贴到搜索框中。$(() => { ... });包装代码的顶部部分仅仅意味着“当页面加载时执行此操作”;简单的Jquery术语。

var searchTimer;
var searchInterval = 1000;


$(() => {
$('#search-box').on('input', (event) => {
clearTimeout(searchTimer);
searchTimer = setTimeout(() => {
searchContacts(event.target.value);
}, (event.target.value.length > 0) ? searchInterval : 0);
});
});


function searchContacts(val) {
console.log('searching: ' + val);
}

const inText = document.getElementById('inText')
const outText = document.getElementById('outText')
const delay = 1000


let timer
inText.addEventListener('input', code => {
clearTimeout(timer);
timer = setTimeout(x => {
outText.innerHTML = inText.value
}, delay, code)
})
<textarea id='inText'>edit this and...</textarea>
<pre id='outText'>see the results after you stop typing for one second</pre>

对于alpine.js用户<input @input.debounce.500ms="fn()">