How to trigger an onkeyup event that's delayed until a user pauses their typing?
You could combine a keypress
event handler with setTimeout
so that you send an Ajax request a set amount of time after a keypress, cancelling and restarting the timer if another keypress occurs before the timer finishes. Assuming you have a textarea with id 'myTextArea' and an Ajax callback function called doAjaxStuff
:
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 );
What you're looking for is called debouncing
. Here is a generic algorithm in native JavaScript:
function debounce(fn, duration) {
var timer;
return function() {
clearTimeout(timer);
timer = setTimeout(fn, duration)
}
}
And here's an example of how to use it with an onkeyup
event:
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>
Another alternative is to use a small jQuery plugin called bindWithDelay. It uses the same setTimeout technique that the accepted answer has, but handles the timeouts transparently so your code is a little easier to read. The source code can be seen on github.
$("#myTextArea").bindWithDelay("keypress", doAjaxStuff, 1000)