用户完成键入而不是按键盘键时运行javascript函数?


463

我想在用户完成在文本框中的输入后触发ajax请求。我不希望它在用户每次输入字母时都运行该函数,因为这会导致很多ajax请求,但是我也不希望他们也必须按下Enter键。

有没有一种方法可以让我检测用户何时完成键入,然后再执行ajax请求?

在这里使用jQuery!戴夫


21
我认为您需要为我们定义“完成输入”。
超现实主义之梦

8
@Surreal Dreams的答案可以满足您的大多数要求,但如果用户在指定的超时时间后再次开始键入内容,则会将多个请求发送到服务器。请参阅下面的答案,其中将每个XHR请求存储在一个变量中,并在发出新请求之前将其取消。实际上,这就是Google在即时搜索中所做的。
Marko,2010年


1
那模糊呢?我猜想当输入元素失去焦点时,用户肯定已经完成了键入操作。
Don P

3
一个简单的谷歌搜索可以给你一个简单的答案:schier.co/blog/2014/12/08/…–
Cannicide

Answers:


686

因此,我猜测完成键入意味着您只需停顿一会儿,例如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
谢谢:)我开始对问题发表评论,意识到我有一个不错的主意。
超现实主义之梦

26
该答案无法正常运行,除非用户输入速度非常慢,否则它将始终在5秒钟后触发。请参阅下面的工作解决方案。

3
如果您在此处遇到麻烦,因为计时器立即触发,请尝试在函数调用周围添加引号:setTimeout('functionToBeCalled',doneTypingInterval);
Largo 2012年

3
doneTyping在示例中看到一些评论,其中回调函数立即运行。这通常是由于不小心()在函数名称之后添加了结果。请注意,它应该读setTimeout(doneTyping,setTimeout(doneTyping(),
安东尼DiSanti

2
@Jessica要使用粘贴,您必须添加“ paste”事件,如下所示:on('keyup paste',
Sergey

397

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

由于偶尔会多次设置typeingTimer(对于快速打字机等,在按下键盘键之前会先按下两次键盘键),因此无法正确清除。

下面的解决方案解决了此问题,并在完成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,但是在这里没有必要。只需将替换为letvar然后将箭头功能替换为常规功能即可。


5
好吧,即使输入为空,仍然调用该函数可能会很有用。如果您想清除某些搜索结果(例如),当输入再次为空白时该怎么办?
rvighne 2014年

7
我认为我们还应该考虑用户仅将字段字符串粘贴到字段中的情况。
Vishal Nair 2014年

10
使用$('#myInput').on('input', function() {,如果你想这与复制和粘贴的工作。如果输入为空,我看不到要检查的地方。假设他想删除他键入的内容将无法进行ajax调用。
billynoah 2014年

3
为什么if ($('#myInput').val)不是if ($('#myInput').val())呢?应该.val是功能吗?
wlnirvana 2014年

4
为什么不使用$(this).val()?
JoelFan 2015年

76

这只是一条线underscore.js防抖动功能:

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

这基本上是说我停止输入500毫秒后的doSomething

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



63
人们称赞“一行代码”是至关重要的,这让我感到惊讶。大。一行代码,需要加载1.1k JS文件。我不会对此表示反对,因为这是一种解决方案。但是一行大胆的事情让我感到不安,并导致代码膨胀。
Wolfie

1
@Wolfie,我同意您对代码膨胀的看法,但是Underscore和Lodash是NPM上依赖的前两个实用程序,因此对于许多人来说,这可能是单行解决方案。
保罗

3
@Paul我同意,如果您出于其他目的加载库,则使用通用代码很好而且实际上是有益的。但是,为了一行代码的目的而加载K的代码效率不高。尤其是随着移动平台变得越来越少的无限数据。而且许多欧元区也通过数据为互联网付费。因此,在合理的时候注意good肿是一件好事。
Wolfie,2013年

我为什么得到: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

Ixg,不,这不会。他使用的clearTimeout函数清除先前的Timeout事件,从而调用API调用。
约翰·史密斯

@JohnSmith,我不同意,最好先清除计时器,然后检查xhr是否为空或您的状况如何,最后通过ajax调用运行计时器
Fleuv

19
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

前两个答案对我都不起作用。所以,这是我的解决方案:

var timeout = null;

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

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

11

我喜欢超现实梦的答案,但是我发现我的“ doneTyping”函数会在每次按键时触发,即,如果您真正快速地键入“ Hello”的话;该函数将触发5次,而不是在您停止键入时仅触发一次。

问题在于javascript setTimeout函数似乎不会覆盖或杀死已设置的任何旧超时,但是如果您自己进行操作,它将起作用!因此,如果设置了typeTimer,我只是在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
}

注意:我希望将其添加为“超现实梦想”答案的注释,但我是新用户,并且信誉不足。抱歉!


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

}

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


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非常感谢。我还添加了另一个答案来解决更复杂的情况(每页多个控件)。
法比安·比格勒

好的解决方案。我正在使用它,而不是在
按下key时

使用keyup事件,对我来说效果很好。谢谢。
思南·艾德姆

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” ...)而不是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);
});

3

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

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

步骤2.在触发keyup事件之前,将清除超时触发到变量定义。

步骤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”,表明该名称可用。
David Zorychta '17

如果搜索栏处于焦点位置,则无需走到外面,过了一段时间,结果将直接显示,因为不再输入打开的内容
P Satish Patro

1

如果用户有必要离开该领域,我们可以在Javascript中使用“ onBlur”代替Onchange

  <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

每页多个计时器

所有其他答案仅适用于一个控件(包括我的其他答案)。如果您每页有多个控件(例如,购物车中的控件),则只会调用用户键入内容的最后一个控件。就我而言,这当然不是所希望的行为-每个控件都应具有自己的计时器。

要解决此问题,您只需要将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() { ... });应该用来触发keyuppaste并且change事件

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

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

  5. (我的决定)我使用匿名函数而不是doneTypingin setTimeout来方便地$(this)从n.4 访问,但是您需要先保存它,例如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

@ThomasOrlita这个答案补充了3个最受好评的答案。看看被接受的一个:1.它不支持paste,不使用this等(我认为这很重要,但我不想迷失在大量评论中)
vladkras

1
这不能回答问题。如果您想评论另一个答案,则有一个评论系统(我现在正在使用它!)
GrayedFox
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.