The KIRUPA orange logo! A stylized orange made to look like a glass of orange juice! Tutorials Coding Exercises Videos Books

FORUMS

Customize Theme


Color

Background


Done

Throttling Chatty Events

by kirupa   |   filed under JavaScript 101

Learn how to keep chatty events like mousemove, touchmove, etc. from executing code unnecessarily!

Many events in JavaScript get fired only once per action. A popular example of such an event is click. For each time you click on something, the click event is fired exactly once:

let myButton = document.querySelector("#myButton");
myButton.addEventListener("click", doSomething, false);

function doSomething(event) {
  console.log("Reacting to a click event!");
}

Some events like mousemove, touchmove, and scroll fire many MANY times for actions such as moving our mouse/finger or scrolling the scrollbar. Because of the large number of times these events fire for what looks like a small number of actions, we consider these events to be chatty.

Now, chatty events by themselves aren’t too bad. They only become a problem when we are listening to a chatty event and getting overwhelmed by the frequency of updates we need to react to:

Reacting to every single chatty event can be wasteful because most things we do won’t require such a high frequency of updates. Most times, we will want to react to a chatty event only once every few seconds, as part of a screen update, or so on. This means we will need a way to throttle our reaction to a chatty event to a more manageable rate depending on what we are doing.

Let’s look at a few ways of doing that in this article

Onwards!

General Approach

First, the bad news. We have no control over how frequently an event is fired. That logic is defined and controlled deep inside our browser runtime’s internals. Now, here is the good news. We do have control over the frequency of work we do as a result of an event getting fired. Key to giving us this control are our old friends, the timers!

Throttling Event Reactions to the Frame Rate

There are many situations where we want to tie our reaction to an event with a visual update. For example, we may want to have something follow our mouse or finger. For these situations, we have our trusty requestAnimationFrame timer function. The following snippet shows how we can use requestAnimationFrame to deal with a chatty event:

document.body.addEventListener("mousemove", throttleEvent, false);

let isFiring = false;

function throttleEvent(event) {
  if (isFiring === false) {
    requestAnimationFrame(() => {
      doSomething(event);
      isFiring = false;
    });
  }
  isFiring = true;
}

function doSomething(event) {
  console.log("Event fired!");
}

There are few things to note here:

  1. We are listening to the chatty mousemove event, and the event handler for this event is throttleEvent
  2. The throttleEvent event handler uses requestAnimationFrame to slow down our reaction (aka the call to doSomething) to every mousemove event that it gets called on
  3. The doSomething function is where we would put any code that is relevant to us reacting to the mousemove event

While the throttleEvent event handler will be called each time our chatty mousemove event fires, the doSomething function that we are using to react to the mousemove is throttled by requestAnimationFrame and gets called with each screen update instead.

Throttling to a Time Delay

Some times, we may want to react to an event more infrequently compared to a screen update. For these situations, we can defer to a setTimeout function and specify the delay between each event reaction. The following code example shows how we can pull this off:

document.body.addEventListener("mousemove", throttleEvent, false);

let isFiring = false;

function throttleEvent(event) {
  if (isFiring === false) {
    setTimeout(() => {
      doSomething(event);
      isFiring = false;
    }, 200);
  }
  isFiring = true;

  console.log("Chatty event");
}

function doSomething(event) {
  console.log("React to event!");
}

Notice that our throttleEvent has a setTimeout with a delay of 200 milliseconds (aka .2 seconds). We can adjust this value depending on how frequently or infrequently we want to react to an event getting fired. For a live example (open as standalone example) of how this code works, take a look at the example below:


Conclusion

When we have code that is doing unnecessary work, nobody wins. Our browser is wasting valuable CPU and battery cycles that we want to conserve. This is especially problematic on mobile devices that are already CPU and battery starved. Now, handling chatty events is one of those fairly straightforward improvements we can make. If we have the opportunity to get some performance gains with little extra effort, who wouldn't jump on that?! See you all next time!

The KIRUPA Newsletter

Thought provoking content that lives at the intersection of design 🎨, development 🤖, and business 💰 - delivered weekly to over 100,000 subscribers!

SUBSCRIBE NOW

Serving you freshly baked content since 1998!
Killer hosting by (mt) mediatemple

Twitter Youtube Facebook Pinterest Instagram Github