Explain Codes LogoExplain Codes Logo

Javascript/jquery: $(window).resize how to fire AFTER the resize is completed?

javascript
debounce
throttle
performance-optimization
Anton ShumikhinbyAnton Shumikhin·Oct 9, 2024
TLDR

To trigger a function after resizing completes, try debouncing in JavaScript. Here's a fundamental implementation:

// You ever have those days where you can't function without coffee? Well, neither can JavaScript. function debounce(fn, delay) { let timer; return () => { clearTimeout(timer); // Because in JavaScript, ketchup is a slow-pouring function. timer = setTimeout(() => fn(), delay); }; } // I calculate window re-sizing the way I used to calculate math tests – after they're done. window.addEventListener('resize', debounce(() => { console.log('Resize event complete!'); }, 250));

This listener fires the console.log function after a 250ms delay following the end of the resize event.

Performance optimization

Resize callbacks can fire dozens of times per second during window resizing which can strain system resources, especially on complexes apps or older devices. Here are various optimization techniques to maintain your app's performance:

Introducing throttle

Throttling is debouncing's less aggressive cousin. It allows for a maximum number of function calls over a given time:

function throttle(fn, wait) { let inThrottle; return function() { if (!inThrottle) { // When not throttling, let loose the horses! Or...function calls. fn(); inThrottle = true; // Much like Godzilla, inThrottle will lay low for a while after wreaking havoc. setTimeout(() => inThrottle = false, wait); } }; } window.addEventListener('resize', throttle(() => { console.log('Resize action fired!'); }, 250));

Unique ids to avoid collisions

When using multiple instances of resize callbacks, ensure each callback has a unique id to keep them from tripping over each other:

const waitForFinalEvent = (() => { const timers = {}; return (callback, ms, uniqueId) => { // If there's no uniqueId, consider yourself "Unique". Like a snowflake. Unique snowflakes are nice. if (!uniqueId) { uniqueId = "UniqueId"; } if (timers[uniqueId]) { clearTimeout(timers[uniqueId]); } timers[uniqueId] = setTimeout(callback, ms); }; })();

Lib-erating your code with Lodash or Underscore.js

If your motto is "Why do it myself when a library can do it for me?", then Lodash or Underscore.js can help with their pre-built debounce functions:

window.addEventListener('resize', _.debounce(() => { console.log('Resize complete!'); }, 250));

Remember, including heavy libraries just for a single debounce function may be overkill, but if you're already using these libraries elsewhere, go ahead!

It's a bird, it's a plane, it's... resizeEnd?

In complex scenarios, a custom event might be appropriate for different parts of your application to detect when resizing ends:

$(window).on('resize', debounce(() => { $(window).trigger('resizeEnd'); }, 250)); $(window).on('resizeEnd', () => { console.log('Resize event custom handler complete!'); });

Using global variables or data attributes for timers

Storing the timer in a global variable or data attribute can be useful to access it from different functions or scopes:

function handleResize() { console.log('Resize event completed globally!'); } $(window).resize(function() { clearTimeout($.data(this, 'resizeTimer')); // Let's keep everyone in the loop via a data attribute. $.data(this, 'resizeTimer', setTimeout(handleResize, 250)); });

By associating the timer with the window itself, you ensure a more efficient management of the delay and reduce potential conflicts.