If you listen for events like mousewheel , resize or scroll , your page could get slow. These events are generated multiple times per second, and if the event handler takes too much time, the browser won’t catch with redrawing the page.

This is what to do if you have such problem.

CSS-Tricks’ article on throttling By default, the event handler is executed on each event. Sometimes, when the handler is taking a long time, it might be acceptable to delay its execution to once per 100-200 ms. This “delaying” can be achieved with the _.throttle utility method.

When is this useful? E.g. if you recalculate the page layout each time the browser resizes. If recalculating layout takes a long time, you could perform it less often.

Before and after:

// Before window.addEventListener('resize', () => { calculateLayout(); // Takes 20 ms }); // After window.addEventListener('resize', _.throttle(() => { calculateLayout(); // Still takes 20 ms, but now runs once in 100 ms }, 100));

About passive: true on GitHub passive: true a flag that’s passed into addEventListener . It tells the browser that the event handler will never call event.preventDefault() . Using this knowledge, the browser can start doing the event action (e.g. scrolling) without waiting for the JS code to finish. This makes the page smoother.

Currently, only touchstart , touchmove and mousewheel event handlers could be made passive. When is this useful? E.g. if you redraw the parallax background on each touch or scroll event. Most likely, it won’t be a problem if the browser starts scrolling before the background finishes redrawing. If so, such event handler could be made passive.

Before and after:

// Before: window.addEventListener('touchstart', updateBackground) // After: window.addEventListener('touchstart', updateBackground, { passive: true })

Use alternative APIs#

For some common tasks, there are alternative APIs that help to perform the task easier and with better performance:

Intersection Observer . It helps if you need to detect when an element goes off the screen or intersects with another element. This API can replace the scroll and resize listeners.

. It helps if you need to detect when an element goes off the screen or intersects with another element. This API can replace the and listeners. window.matchMedia . This API can notify you when a specific media query becomes true. Helps if you need to e.g. update the page layout when the viewport reaches a specific width. It can replace the resize listeners.

. This API can notify you when a specific media query becomes true. Helps if you need to e.g. update the page layout when the viewport reaches a specific width. It can replace the listeners. position: sticky . This CSS property lets you stick an element to the top when it reaches an edge of the viewport. Helps if you need to e.g. make a sticky page header. It can replace the scroll listeners that switch the element from position: static to position: fixed and vice versa.

. This CSS property lets you stick an element to the top when it reaches an edge of the viewport. Helps if you need to e.g. make a sticky page header. It can replace the listeners that switch the element from to and vice versa. At the moment of July 2017, the API is not yet well-supported. However, there’s a polyfill Resize Observer. It helps if you need to detect when an element gets resized. This API can replace the scroll and resize listeners. Thanks to Vitali Kuzmich for mentioning this approach.

Optimize the code itself#

Why this 10 ms border is important You can also make the event handler itself run faster. It should take not more than 10 ms to run.

To analyze how much time your handler takes, use Web Performance API or the Performance tab in Chrome DevTools:

The optimization approach depends on your code. Try caching some data or skipping some computations. If you use React, you can try dropping parts of the render tree with shouldComponentUpdate.

And this is what won’t work:#

requestAnimationFrame . It doesn’t help with slow event handlers and, by my tests, doesn’t bring any benefits for fast ones. Seems like it’s only useful for scheduling JS animations.

. It doesn’t help with slow event handlers and, by my tests, doesn’t bring any benefits for fast ones. Seems like it’s only useful for scheduling JS animations. Event delegation. It optimizes the memory usage, not the speed.

That’s it. Optimize.

Related