Evitando errores comunes con el evento resize en JavaScript

El evento resize de la ventana parece sencillo, hasta que tu aplicación empieza a ralentizarse. Si alguna vez te has preguntado por qué tu código JavaScript responsivo causa problemas de rendimiento, probablemente te estés enfrentando a uno de los errores más comunes del evento resize de JavaScript que afectan a las aplicaciones frontend.
Este artículo explora por qué los eventos resize pueden paralizar el rendimiento, cómo optimizarlos con throttling y debouncing, y cuándo omitir JavaScript por completo utilizando soluciones CSS modernas y APIs.
Puntos Clave
- Los eventos resize se disparan cientos de veces por segundo durante el redimensionamiento de la ventana, causando problemas graves de rendimiento
- El throttling y debouncing son técnicas esenciales para limitar la frecuencia de ejecución de los manejadores de eventos
- Las alternativas modernas como la API ResizeObserver y las CSS container queries a menudo proporcionan mejor rendimiento
- La limpieza adecuada de los event listeners previene memory leaks en aplicaciones de producción
El Costo Oculto de Rendimiento de los Eventos Resize de JavaScript
Cuando un usuario arrastra para redimensionar la ventana de su navegador, el evento resize no se dispara una vez—se dispara continuamente. Un simple arrastre de ventana puede activar el manejador de eventos cientos de veces por segundo, inundando el hilo principal con llamadas de función.
// Esto se registra cientos de veces durante una sola acción de redimensionamiento
window.addEventListener('resize', () => {
console.log(`Tamaño de ventana: ${window.innerWidth}x${window.innerHeight}`);
});
Cada ejecución del evento bloquea el hilo principal, impidiendo que el navegador maneje otras tareas críticas como actualizaciones de renderizado o procesamiento de interacciones del usuario. ¿El resultado? Animaciones entrecortadas, interfaces que no responden y usuarios frustrados.
Por Qué los Errores Comunes del Evento Resize de JavaScript Importan para el Rendimiento
Disparo Excesivo de Eventos y Bloqueo del Hilo Principal
El evento resize se dispara por cada cambio de píxel durante el redimensionamiento de la ventana. Si tu manejador realiza cálculos complejos o manipulaciones del DOM, esencialmente estás ejecutando operaciones costosas cientos de veces por segundo.
Considera este patrón común:
window.addEventListener('resize', () => {
const elements = document.querySelectorAll('.responsive-element');
elements.forEach(el => {
// Cálculos complejos para cada elemento
el.style.width = calculateOptimalWidth(el);
});
});
Este código recalcula y actualiza múltiples elementos continuamente durante el redimensionamiento, creando un cuello de botella de rendimiento.
Layout Thrashing: El Asesino Silencioso del Rendimiento
El error más insidioso del evento resize de JavaScript ocurre cuando lees las dimensiones de un elemento e inmediatamente escribes nuevos estilos. Este patrón, llamado layout thrashing, fuerza al navegador a recalcular los layouts de forma síncrona:
window.addEventListener('resize', () => {
// Fuerza el cálculo del layout
const width = element.offsetWidth;
// Invalida el layout
element.style.width = (width * 0.8) + 'px';
// Fuerza otro cálculo del layout
const height = element.offsetHeight;
});
Cada lectura de dimensión activa un recálculo completo del layout, multiplicado por cientos de eventos resize.
Técnicas de Optimización Esenciales: Throttling y Debouncing
Implementando Throttle para Eventos Resize
El throttling limita la frecuencia con que se ejecuta tu manejador de resize, típicamente a 60fps (cada 16ms) o menos:
function throttle(func, delay) {
let lastExecTime = 0;
let timeoutId;
return function (...args) {
const currentTime = Date.now();
if (currentTime - lastExecTime > delay) {
func.apply(this, args);
lastExecTime = currentTime;
} else {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
lastExecTime = Date.now();
}, delay - (currentTime - lastExecTime));
}
};
}
const throttledResize = throttle(() => {
// Esto se ejecuta como máximo una vez cada 100ms
updateLayout();
}, 100);
window.addEventListener('resize', throttledResize);
Discover how at OpenReplay.com.
Debouncing para Acciones Completas de Redimensionamiento
El debouncing espera hasta que el redimensionamiento se detenga antes de ejecutarse:
function debounce(func, delay) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
const debouncedResize = debounce(() => {
// Se ejecuta 250ms después de que el redimensionamiento se detiene
recalculateLayout();
}, 250);
window.addEventListener('resize', debouncedResize);
// No olvides la limpieza
// window.removeEventListener('resize', debouncedResize);
Alternativas Modernas a los Eventos Window Resize
Soluciones CSS-First: Media Queries y Container Queries
A menudo, puedes evitar completamente los errores del evento resize de JavaScript usando CSS:
/* Media queries para responsividad basada en la ventana */
@media (max-width: 768px) {
.sidebar { display: none; }
}
/* Container queries para responsividad basada en componentes */
.card-container {
container-type: inline-size;
}
@container (min-width: 400px) {
.card { grid-template-columns: 1fr 1fr; }
}
Para detección de media queries basada en JavaScript, usa matchMedia:
const mediaQuery = window.matchMedia('(max-width: 768px)');
mediaQuery.addEventListener('change', (e) => {
// Se dispara solo al cruzar el breakpoint
if (e.matches) {
showMobileMenu();
}
});
API ResizeObserver: La Alternativa Amigable con el Rendimiento
ResizeObserver proporciona monitoreo de tamaño específico de elementos sin penalizaciones de rendimiento:
const resizeObserver = new ResizeObserver(entries => {
for (const entry of entries) {
// El navegador proporciona el tamaño—sin reflow forzado
const { width, height } = entry.contentRect;
updateElementLayout(entry.target, width, height);
}
});
resizeObserver.observe(document.querySelector('.responsive-container'));
// Limpieza cuando termine
// resizeObserver.disconnect();
Mejores Prácticas para Manejo de Resize Listo para Producción
- Siempre limpia los event listeners para prevenir memory leaks
- Elige la herramienta correcta: Usa CSS para estilos, ResizeObserver para monitoreo de elementos, y eventos resize con throttle solo cuando sea necesario
- Mide el impacto en el rendimiento usando el panel Performance de Chrome DevTools
- Considera listeners pasivos para mejor rendimiento de scroll cuando sea aplicable
Conclusión
Al entender estos errores comunes del evento resize de JavaScript e implementar las soluciones apropiadas, puedes construir interfaces responsivas que funcionen suavemente en todos los dispositivos. La clave es elegir el enfoque correcto para tu caso de uso específico—ya sean soluciones basadas en CSS, APIs modernas, o manejadores de eventos debidamente optimizados. Comienza con CSS donde sea posible, usa ResizeObserver para monitoreo específico de elementos, y reserva los eventos resize con throttle o debounce para casos donde el monitoreo a nivel de ventana sea verdaderamente necesario.
Preguntas Frecuentes
El throttling limita la ejecución a un intervalo fijo durante el redimensionamiento continuo, ejecutándose regularmente mientras el usuario arrastra. El debouncing espera hasta que el redimensionamiento se detiene completamente antes de ejecutarse una vez. Usa throttling para actualizaciones en tiempo real y debouncing para cálculos finales.
ResizeObserver tiene excelente soporte en navegadores modernos incluyendo Chrome, Firefox, Safari y Edge. Para navegadores más antiguos, usa un polyfill o recurre a eventos resize con throttle con detección de características para asegurar compatibilidad.
Usa container queries cuando el estilo dependa del tamaño de un elemento en lugar del viewport. Son perfectas para diseños basados en componentes, layouts de tarjetas y tipografía responsiva sin sobrecarga de JavaScript o problemas de rendimiento.
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.