5

I have a simple input box. When I write something, I want it to be delayed. The problem I have is after the delay when writing characters very fast it calls the console.log multiple times.

What happened now

I type a and wait. Then I type b c d fast and wait. Then e f fast and wait. It catches up which I don't want. I want it to collect what I type, but not output it until the delay is done.

a
.
.
.
b c d
b c d
b c d
.
.
.
e f
e f

What I want to happen

a
.
.
.
b c d
.
.
.
e f

var searchtimer;

window.addEventListener("DOMContentLoaded", () => {
  document.querySelector("#search").addEventListener("input", (e) => {
    searchtimer = setTimeout(() => {
      console.log(e.target.value);
      clearTimeout(searchtimer);
    }, 1000);
  });
});
<input id="search" type="text">
Nguyễn Văn Phong
  • 13,506
  • 17
  • 39
  • 56
Jens Törnell
  • 23,180
  • 45
  • 124
  • 206

2 Answers2

4

Your expected behavior looks like debounce.

It seems to me that you should clearTimeout before creating the new one.

var searchtimer;
window.addEventListener("DOMContentLoaded", () => {
  document.querySelector("#search").addEventListener("input", (e) => {
    clearTimeout(searchtimer); // <--- The solution is here
    searchtimer = setTimeout(() => {
      console.log(e.target.value);
    }, 1000);
  });
});
<input id="search" type="text">

More detailed explanation:

  • It is basically a way for eliminating unwanted signals from an input. So if the defined duration hasn't passed, the previous action should be eliminated by clearTimeout(searchtimer);
  • Keynote: The operator keeps track of the most recent value.

Read post "throttleTime vs debounceTime in RxJS" to understand in detail.

Nguyễn Văn Phong
  • 13,506
  • 17
  • 39
  • 56
2

Solution with step by step explanation

So you need to debounce.

The below code shows how a function is executed only when the time difference between 2 keystrokes is at least 2 seconds.

let count=0;

//If we call this directly from the HTML, the function will be 
// fired for every click, which hinders the performance
let myFunction =()=>{
  document.querySelector("#demo").innerHTML = "Hello World "+ ++count ;
}

//So we'll call this debouncing wrapper function from the HTML instead
let myFunc=letsDebounce(myFunction,2000);

//The wrapper calls setTimeout to execute its argument fn after
// the specified delay, but if the triggering event fires again
// before the setTimeout duration finishes, then the timer gets reset
// so the previous call is ignored
function letsDebounce(fn,d){
  let timer;
  return function(){
    clearTimeout(timer);
    timer=setTimeout(fn,d);
  }
}
<button onclick="myFunc()">Debounce</button>

<p id="demo"></p>
Cat
  • 4,141
  • 2
  • 10
  • 18
  • 1
    myjscoffee, I edited the comments to be (hopefully) more clear, and added a reference link. The only change I made to the code itself is in the line `timer=setTimeout(fn,d);`. If you don't think these changes are an improvement, feel free to revert to your original version. – Cat Mar 08 '21 at 14:41
  • @Cat Sure , thanks for making it more readable and improving the post – Ravi Teja Muddada Mar 09 '21 at 02:23