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

Posted

技术标签:

【中文标题】当用户完成输入而不是按键时运行javascript函数?【英文标题】:Run javascript function when user finishes typing instead of on key up? 【发布时间】:2011-05-12 07:57:30 【问题描述】:

我想在用户输入完文本框后触发 ajax 请求。我不希望它在每次用户输入字母时都运行该函数,因为这会导致大量 ajax 请求,但我也不希望他们必须点击 enter 按钮。

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

在这里使用 jQuery! 戴夫

【问题讨论】:

我认为您需要为我们定义“完成输入”。 虽然@Surreal Dreams 的回答满足了您的大部分要求,但如果用户在指定超时后再次开始输入,则会向服务器发送多个请求。请参阅下面的答案,它将每个 XHR 请求存储在一个变量中,并在启动一个新请求之前取消它。这实际上是 Google 在他们的 Instant 搜索中所做的。 选择的答案不正确有几个原因: 1. 它总是在 5 秒后触发,即使用户正在输入。 2. 它不会等到用户按要求完成输入。 3. 如上面@Marko 所述,它会触发多个请求。请参阅下面的更正答案。 模糊呢?我猜当输入元素失去焦点时,用户肯定已经完成了输入。 一个简单的谷歌搜索就能得到简单的答案:schier.co/blog/2014/12/08/… 【参考方案1】:

所以,我猜想完成打字意味着你只需停下来一会儿,比如 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

【讨论】:

谢谢 :) 我开始输入我对这个问题的评论并意识到我有一个不错的想法。 此答案无法正常工作,除非用户键入速度非常慢,否则它将始终在 5 秒后触发。请参阅下面的工作解决方案。 如果你在这里遇到麻烦,因为计时器会立即触发,请尝试在函数调用周围添加引号:setTimeout('functionToBeCalled', doneTypingInterval); 将数据复制/粘贴到字段中时不起作用。 我看到一些 cmets 的回调函数(示例中为 doneTyping)正在立即运行。这通常是由于在函数名后面不小心包含() 造成的。请注意,它应该是setTimeout(doneTyping, 而不是setTimeout(doneTyping(),【参考方案2】:

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

因为 typingTimer 有时会设置多次(在快速打字机等情况下触发 keydown 之前按了两次 keyup),因此无法正确清除。

下面的解决方案解决了这个问题,并在完成后按照 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 并将箭头函数替换为常规函数即可。

【讨论】:

好吧,即使输入为空,仍然调用该函数可能很有用。如果您想在输入再次为空时清除某些搜索结果(例如)怎么办? 我认为我们还应该考虑用户只是将字符串粘贴到字段中的情况。 如果您希望它与复制和粘贴一起使用,请使用 $('#myInput').on('input', function() 。如果输入为空,我看不到检查的意义。假设他想删除他输入的内容,这将无法进行 ajax 调用。 为什么不使用 $(this).val()? 对我来说它立即调用函数:/ 不是在 5 秒后,我尝试增加时间,但同样的事情发生【参考方案3】:

这只是一行underscore.js去抖功能:

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

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

欲了解更多信息: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 的代码效率不高。尤其是随着移动平台变得越来越少无限数据。许多欧元区也通过数据支付互联网费用。因此,在合理的情况下注意膨胀是件好事。 你可以得到 debouce() 函数,npmjs.com/package/debounce,如果这就是你所需要的。效果很好。【参考方案4】:

迟到的答案,但我添加它是因为它是 2019 年,这完全可以使用漂亮的 ES6 来实现,没有第三方库,而且我发现大多数高度评价的答案都很庞大,而且变量太多。

取自this excellent blog post.的优雅解决方案

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

【讨论】:

这真的应该是今年的最佳答案 已编辑以进一步删除不需要的变量 - 可以通过将普通函数而不是箭头函数传递给 setTimeout() 来避免封装 this 如何在事件处理函数中获取事件? 为什么这在事件侦听器之外不起作用?例如如果我运行debounce(() => alert("test"), 1000),并且我在1000ms内没有再次运行它,它不应该运行回调和警报测试吗?【参考方案5】:

是的,您可以为每个触发 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 回调,它仍然会向服务器发送请求。 Ixg,不,这不会。他使用的 clearTimeout 函数会清除之前的 Timeout 事件,从而调用 API 调用。 @JohnSmith,我不同意最好先清除计时器,然后检查 xhr 是否为空或其他情况,最后使用 ajax 调用运行计时器【参考方案6】:
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 似乎可以解决问题。【参考方案7】:

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

var timeout = null;

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

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

【讨论】:

【参考方案8】:

修改接受的答案以处理粘贴等其他情况:

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

【讨论】:

【参考方案9】:

我喜欢 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

注意我本来希望将其添加为对超现实梦的答案的评论,但我是新用户并且没有足够的声誉。对不起!

【讨论】:

【参考方案10】:

声明以下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);
);    

【讨论】:

你的解决方案太棒了,兄弟! @Developer 非常感谢。我还添加了另一个答案来解决更复杂的场景(每页多个控件)。 很好的解决方案。我使用这个,但不是 keydown 我使用“输入”事件【参考方案11】:

我认为在这种情况下不需要 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 上的第一次回复,所以我希望有一天这对某人有所帮助:)

【讨论】:

【参考方案12】:

同意@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);
);

【讨论】:

【参考方案13】:

我在我的列表中实现搜索,并且需要它基于 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

您会注意到在实现此功能时不需要使用任何计时器。

【讨论】:

【参考方案14】:

好吧,严格来说不是,因为计算机无法猜测用户何时完成输入。您当然可以在按键时触发计时器,并在每次后续按键时将其重置。如果计时器到期,则用户在计时器持续时间内没有输入 - 您可以称之为“完成输入”。

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

(当然,除非你可以从数据中看出它们何时完成)

【讨论】:

【参考方案15】:

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

var typingTimer;
var doneTypingInterval = 500;

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

function doneTyping () 
    // code here

【讨论】:

【参考方案16】:

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

步骤 1.将超时设置为 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);
;

【讨论】:

问题已经回答了……和这个很相似【参考方案17】:

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

【讨论】:

【参考方案18】:

为什么不直接使用 onfocusout?

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

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

【讨论】:

onfocusout 在提出这个问题时得到的支持很差(大约 7 年前),而且 onfocusout 的效果也会有所不同。您将不得不等待用户将焦点留在元素上,而使用超时/去抖动解决方案,一旦用户停止输入并且用户不需要切换焦点,它就会“触发”。一个用例示例是其中一种注册表单,当用户停止输入潜在用户名时,表单字段右侧会出现一个复选标记或“X”,表示该名称可用。 如果搜索栏在焦点上,则不需要任何人出来,一段时间后会直接显示结果,因为没有打开正在输入了【参考方案19】:

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

这与“停止输入”不同,如果您关心用户输入一堆内容然后坐在那里而文本框仍然聚焦的情况。

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

【讨论】:

【参考方案20】:

每页多个计时器

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

要解决这个问题,您只需将一个 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);

【讨论】:

【参考方案21】:

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

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

如果没有必要设置计时器将是一个不错的选择。

【讨论】:

【参考方案22】:

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

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

【讨论】:

【参考方案23】:

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

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

【讨论】:

在发送 ajax 请求之前进行有限的验证不是一个坏主意。【参考方案24】:

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

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

【讨论】:

【参考方案25】:

如果您需要等到用户完成输入,请使用以下代码:

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

【讨论】:

【参考方案26】:

简单易懂。

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

【讨论】:

【参考方案27】:

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

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

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

【讨论】:

【参考方案28】:

如果有人在寻找 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();
        );
    ;
]);

【讨论】:

【参考方案29】:

你们听说过 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);
)

【讨论】:

【参考方案30】:

我需要我的来运行特定控件,这对我有用:

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

【讨论】:

以上是关于当用户完成输入而不是按键时运行javascript函数?的主要内容,如果未能解决你的问题,请参考以下文章

数据表全局搜索输入键的按键而不是任何按键按键

JavaScript HTML DOM 事件

JavaScript HTML DOM 事件

JavaScript HTML DOM事件

第6天-javascript事件

iPhone:当用户单击主页按钮时,是不是可以关闭应用程序而不是在后台运行?