如何触发延迟到用户暂停输入的 onkeyup 事件?

Posted

技术标签:

【中文标题】如何触发延迟到用户暂停输入的 onkeyup 事件?【英文标题】:How to trigger an onkeyup event that's delayed until a user pauses their typing? 【发布时间】:2010-12-09 21:17:58 【问题描述】:

我有一个文本区域,人们(自然地)在其中输入一些文本,我想这样做,以便不时发出 AJAX 请求以获取有关文本区域的一些建议(如堆栈溢出的相关问题,但对于文本区域,而不是文本输入)。问题是我不能在每次按键时都执行 AJAX 请求(这将是无用的并且非常消耗资源),而且我不确定最有效的方法是什么(每 X 个单词?每 X 秒?还是别的什么?)。

最好的方法是什么?

提前谢谢你。

【问题讨论】:

jQuery .keyup() delay的可能重复 @kenorb 这个问题实际上是第一个,你引用的问题被认为是这个问题的重复,但管理员不想关闭这个问题,因为标题更好,它有大约 132931 次查看,即相当了不起。 这个老了没关系,另一个有更多的投票和观点,见:Should I flag a question as duplicate if it has received better answers?。标题可以随时编辑为更好的标题。他们可以一直是merged。 很酷,我 2009 年的问题在 2016 年得到审查。谈论彻底的审核。 【参考方案1】:

您要查找的内容称为debouncing。这是原生 javascript 中的通用算法:

function debounce(fn, duration) 
  var timer;
  return function() 
    clearTimeout(timer);
    timer = setTimeout(fn, duration)
  

下面是一个如何将它与onkeyup 事件一起使用的示例:

function debounce(fn, duration) 
  var timer;
  return function()
    clearTimeout(timer);
    timer = setTimeout(fn, duration);
  


const txt = document.querySelector('#txt')
const out = document.querySelector('#out')
const status = document.querySelector('#status')

const onReady = () => 
  txt.addEventListener('keydown', () => 
    out.classList.remove('idle')
    out.classList.add('typing')
    status.textContent = 'typing...'
  )
  
  txt.addEventListener('keyup', debounce(() => 
    out.classList.remove('typing')
    out.classList.add('idle')
    status.textContent = 'idle...'
  , 800))


document.addEventListener('DOMContentLoaded', onReady)
#wrapper
  width: 300px;


input
  padding: 8px;
  font-size: 16px;
  width: 100%;
  box-sizing: border-box;


#out
  margin: 10px 0;
  padding: 8px;
  width: 100%;
  box-sizing: border-box;


.typing
  background: #A00;
  color: #FFF;


.idle
  background: #0A0;
  color: #FFF;
<div id="wrapper">
  <input id="txt" placeholder="Type here" />
  <div id="out">Status: <span id="status">waiting...</span></div>
</div>

【讨论】:

这应该是我接受的解决方案...简单,干净,它可以让你在你想要的每个事件监听器上使用它...【参考方案2】:

如果您使用的是 lodash.js(或 underscore.js),您可以使用 debounce function。

示例(用于 keyup 的 jQuery):

$('#myTextArea').keyup(_.debounce(function() 
  alert('hello');
, 500));

【讨论】:

【参考方案3】:

如果您在网站上大量使用此功能并且不想跟踪所有 setTimeout 引用等,那么我将其打包成一个可用的插件here。

该插件为普通的 $.ajax 方法添加了一些选项,用于缓冲和取消以前的 ajax 调用。

【讨论】:

【参考方案4】:

另一种选择是使用一个名为bindWithDelay 的小型jQuery 插件。它使用与接受的答案相同的 setTimeout 技术,但透明地处理超时,因此您的代码更易于阅读。 source code可以在github上看到。

$("#myTextArea").bindWithDelay("keypress", doAjaxStuff, 1000)

【讨论】:

【参考方案5】:

您可以将keypress 事件处理程序与setTimeout 结合使用,这样您就可以在按键后的一段时间内发送Ajax 请求,如果在计时器完成之前发生另一个按键,则取消并重新启动计时器。假设你有一个 id 为 'myTextArea' 的 textarea 和一个名为 doAjaxStuff 的 Ajax 回调函数:

function addTextAreaCallback(textArea, callback, delay) 
    var timer = null;
    textArea.onkeypress = function() 
        if (timer) 
            window.clearTimeout(timer);
        
        timer = window.setTimeout( function() 
            timer = null;
            callback();
        , delay );
    ;
    textArea = null;


addTextAreaCallback( document.getElementById("myTextArea"), doAjaxStuff, 1000 );

【讨论】:

这是公认的“正确”方法。说得好。 感谢蒂姆,出色的解决方案:D 如果您希望能够捕获退格键事件,请使用 onkeyup。 @barkmadley:如果您不想阻止其默认操作,那很好。您可以在按键处理程序中可靠地捕获退格键事件并防止默认操作。 @Tim Down:代码运行良好,但有一个问题:如果您在 textarea 具有焦点时将 Alt-Tab(在 Windows 中)切换到另一个应用程序,然后再次返回 Alt-Tab,回调函数被解雇。它不应该,因为没有按下任何键。我不知道如何解决这个问题。【参考方案6】:

我个人会使用setInterval 来监视文本区域的更改,并且仅在检测到更改时执行 AJAX 回调。每一秒都应该足够快和足够慢。

【讨论】:

这需要 setInterval 永远运行。 Tim 的解决方案更加优雅,仅在有人实际与文本框交互时运行。 永远运行,直到页面被卸载。此外,如果您想在用户输入时提供一些反馈,我的解决方案会更好,因为如果用户输入速度超过每秒一个键,它就有机会发送请求。我并不是说 Tim 的解决方案不优雅,但在某些情况下它不是取决于应用程序的解决方案。【参考方案7】:

如果您对 jQuery 解决方案感兴趣,jQuery Suggest 是一个很好的实现。

每次都重新发明***是没有意义的。

【讨论】:

这对我不起作用,因为我不想用所提出的建议来更新 textarea 或任何内容。我只想发出一个 AJAX 请求,以获取页面中另一个元素的内容、每 X 次击键或用户输入的每 X 秒,或其他内容。只是想知道什么是最有效的选择。

以上是关于如何触发延迟到用户暂停输入的 onkeyup 事件?的主要内容,如果未能解决你的问题,请参考以下文章

Javascript onkeyup延迟函数调用[重复]

苹果手机输入中文不会触发onkeyup事件

监听input变化的事件

OnKeyPress、onKeyDown、onKeyUp 事件在 Android 下的 Firefox/Chrome 中在键入字母时未触发

文本框输入内容立即触发事件

text输入框改变事件