TypeOfNaN

How to Execute a Function After the User Stops Typing in JavaScript

Nick Scialli
March 04, 2021

Executing a function after a certain amount of inactivity is known as debouncing. Debouncing can be helpful for many reasons. One of the most popular applications in web development is to execute a search or filter some results after a user has stopped typing text in a textbox.

A Quick Example: Filtering Cities

Let’s say we have a list of cities in our document we want to filter. We don’t want to execute the filter function constantly, but only after the user is “done” typing. Since we don’t truly know when the user is done, we can make a guess that if the user pauses for one second, we are safe to execute the function. (This may vary depending on your use case).

Ultimately, we want our effect to look like this:

filtering cities

Our corresponding HTML looks like this:

<body>
  <input type="text" id="city-filter" />
  <ul id="city-list">
    <li>Boston</li>
    <li>New Brunswick</li>
    <li>New York</li>
    <li>Philadelphia</li>
    <li>Washington</li>
    <li>Wilmington</li>
  </ul>
</body>

How This Will Work

The idea behind debouncing is that we have a global JavaScript variable that will hold a reference to a setTimeout.

Then, we have a keyup event listener on our input. When this event is fired, we clear the global timer (if it’s running). This means the timer will only fire if it doesn’t get cleared before the specified timeout—in other words, it only fires if there is no keyup event before the timeout!

Here’s how this looks in JavaScript.

let timer;
const input = document.querySelector('#city-filter');
input.addEventListener(function (e) {
  // Clears any outstanding timer
  clearTimeout(timer);
  // Sets new timer that may or may not get cleared
  timer = setTimeout(() => {
    // Only fires if not cleared
  }, 1000);
});

Filling in the Blanks for Our Use Case

Now all we have to do is fill in the blanks for our use case. I’ll do this by iterating over all the children of the ul #city-list element and updating the style.display property accordingly.

let timer;
const input = document.querySelector('#city-filter');
input.addEventListener(function (e) {
  // Clears any outstanding timer
  clearTimeout(timer);
  // Sets new timer that may or may not get cleared
  timer = setTimeout(() => {
    const items = document.querySelector('#city-list').children;
    for (let item of items) {
      item.style.display = item.textContent.includes(e.target.value)
        ? 'list-item'
        : 'none';
    }
  }, 1000);
});

And that’s really it!

The Entire Code

The code for a full application you can run yourself is as follows:

<!DOCTYPE html>
<html>
  <head>
    <title>Debouncing in JavaScript</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <input type="text" id="city-filter" />
    <ul id="city-list">
      <li>Boston</li>
      <li>New Brunswick</li>
      <li>New York</li>
      <li>Philadelphia</li>
      <li>Washington</li>
      <li>Wilmington</li>
    </ul>

    <script>
      let timer;
      const input = document.querySelector('#city-filter');
      input.addEventListener('keyup', function (e) {
        clearTimeout(timer);
        timer = setTimeout(() => {
          const items = document.querySelector('#city-list').children;
          for (let item of items) {
            item.style.display = item.textContent.includes(e.target.value)
              ? 'list-item'
              : 'none';
          }
        }, 1000);
      });
    </script>
  </body>
</html>

And you can see it in action on CodeSandbox.

Nick Scialli

Nick Scialli is a senior UI engineer at Microsoft.

© 2024 Nick Scialli