jQuery键盘按键延迟检测

8
希望这对某个人来说是一个快速简单的问题。我相当新于使用大量的javascript/jquery,我有以下设置从数据库中提取客户名称,并在用户完成输入客户ID后显示它。
所有工作都很好, 但它在每一个按键按下时搜索。我知道我可以把它改成模糊焦点, 但我希望它能随着输入动态搜索,进行延迟
这是当前的代码:
function postData(){
    var id = $('#id').val();

    $.post('inc/repairs/events-backend.php',{id:id},   
        function(data){
        $("#display_customer").html(data);
    });
    return false;
}

$(function() {
    $("#id").bind('keyup',function() {postData()});
});

如您所见,它绑定到每个keyup事件,这意味着如果您搜索得太快,可能会导致错误的结果,因为它仍在加载中(即如果未找到匹配项,则显示错误,并且快速输入会使用户需要回退并重新输入最后一个数字)。
有人可以帮助添加延迟到现有代码中,并尽可能解释一下您所做的更改,我不想只是复制和粘贴而不理解。
---- 编辑 ----
这是我完成的代码,谢谢大家!
function postData(){
    var id = $('#id').val();

    $.post('inc/repairs/events-backend.php',{id:id},   
        function(data){
        $("#display_customer").html(data);
    });
    return false;
}

$(function() {
    var timer;
    $("#id").bind('keyup input',function() {
        timer && clearTimeout(timer);
        timer = setTimeout(postData, 300);
    });
});

1
请查看以下内容:https://dev59.com/0XI-5IYBdhLWcg3wVWvv - Sudhir Bastakoti
4个回答

8

您应该了解setTimeoutclearTimeout函数:

$(function() {
    var timer;
    $("#id").on('keyup',function() {
        timer && clearTimeout(timer);
        timer = setTimeout(postData, 300);
    });
});

基本上我们所做的是,不是立即执行postData函数,而是将其传递给setTimeout,以便在一定时间后触发它(在这种情况下为300毫秒)。

setTimeout函数返回一个计时器ID,然后我们可以将其传递给clearTimeout以取消该调用的运行。在每次按键之前,在设置新计时器之前,我们首先检查是否已设置计时器。如果有,则在启动新计时器之前取消旧计时器。


为了进一步提升性能,你还可以在每次按键时中止 AJAX 调用,以防用户在计时器触发后但 AJAX 请求返回前的 300+ 毫秒内输入了内容。

附言:你应该查看Ben Alman的节流/防抖插件, 它在这里可以完美地工作。


非常感谢您的快速回复,我理解了并将对此进行少量研究。实施它,立即成功。我曾经看过其他使用相同技术的延迟想法,但创建了两个额外的函数,所以再次非常感谢! - Tarquin

7

看一下Underscore的debounce函数 - 它使得这种行为非常简单:

$(function() {
  $('#id').on('keyup input', _.debounce(postData, 500))
}

这样,只有在用户停止输入 500ms(或者你设置的防抖等待时间)后,才会调用 postData 方法。

另外,在现代浏览器中,我悄悄加入了输入事件来捕获额外的更改,例如复制粘贴,除了按键操作。


我喜欢这段代码可以与输入一起使用,通常我们使用CTRL + V,这意味着它会检测到并发出POST请求,但是如果有人右键单击并使用粘贴,这也可能很有用。我必须说我确实使用了Joseph建议的代码,因为它非常干净,所以如果有一种简单的方法将输入集成进去,我可能会这样做。谢谢你的提示! - Tarquin
2
Joseph的方法应该完全可以使用input事件(这与实现_.debounce的方式几乎相同)-只需将input添加到事件列表中即可! - Nevir
你为什么不直接将 postData 函数传递给 debounce 呢? - Joseph Silber
是的,我查了一下,不确定所需的语法,但真的很容易。再次感谢您关于输入的提示,值得添加! :) - Tarquin
哈,我没有不直接传递它的好理由><正在修复! - Nevir

2

我建议在键盘输入后稍微延迟一下搜索,但您也需要中止最后一次请求,因为您无法预测它们的执行顺序(或者您可以使用ajax队列)。

以下是三个部分的说明:

延迟

setTimeout包装postData回调函数。将setTimeout的返回值存储为$("#id").data('timeout'),每次调用时调用clearTimeout,以便事件仅在快速输入字符的情况下触发一次。

中止

将上一次请求存储在$("#id").data('lastRequest')或类似变量中。每次调用postData时,运行$("#id").data('lastRequest').abort()。将$.post()的返回值存储在该数据字段中。为了避免错误,请进行初始化:

$("#id").data('lastRequest', {abort: function () {}));

Queue

$("#id").data('lastRequest', true);
...
$.when($("#id").data('lastRequest')).then(function () {
   $("#id").data('lastRequest', $.post(...));
});

延迟完成后立即执行POST请求。数据库返回结果,一切都完成了。使用队列会带来什么好处呢?我努力改进我的代码,欢迎任何建议! - Tarquin
1
@Tarquin 队列操作的原因是因为你无法保证POST请求以任何特定顺序完成。通过队列,您可以强制执行顺序,但仍使POST请求对客户端异步。 - Explosion Pills
哦,所以如果我发布信息,由于某些原因服务器响应不够快,那么改变我输入的内容也会被发布。使用当前的代码可能会出现这两种结果之一,对吗? - Tarquin
1
@Tarquin 没错,尽管如果你经常中止操作,这并不一定会发生。我认为我的队列代码也有问题,因为由于请求以错误的顺序完成,它仍然可能出错,但你可以使用数组和 $.when.apply,或者使用类似于此的东西:https://gist.github.com/3818056 - Explosion Pills
我会调查一下并弄清楚它是否对于这个特定的实现是必要的,然而对于大型项目来说,我认为这将非常有用。我认为它在处理大量流量时变得越来越重要。 - Tarquin

0
可能有更好的方法,但这是我脑海中想到的一个解决方案。
window.currentKeyup = 0;
$("#id").bind('keyup',function() {
    window.currentKeyup++;
    var keyup = window.currentKeyup;
    setTimeout(function(){
        if( keyup == window.currentKeyup ){
            postData();
        }
    },3000);
});

这将导致在每次按键时调用 postData - 这将会有大量的POST请求! - Nevir
我不认为在用户输入时条件语句会是假的,postData将在最后一次松开按键后仅3秒后被调用。 - pierdevara
它将在第一次按键弹起后3秒钟被调用;接下来的每次按键弹起 - 如果用户在最初的3秒钟后仍在输入,则会发出多个帖子,我认为是这样? - Nevir
本地变量将保持不变,而全局变量将增加自第一次键盘松开以来的按键次数,因此if语句将为false。 - pierdevara

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接