Éviter les pièges de l'événement resize en JavaScript

L’événement resize de la fenêtre semble simple—jusqu’à ce que votre application commence à ramer. Si vous vous êtes déjà demandé pourquoi votre code JavaScript responsive cause des problèmes de performance, vous rencontrez probablement l’un des pièges les plus courants de l’événement resize JavaScript qui affligent les applications frontend.
Cet article explore pourquoi les événements resize peuvent paralyser les performances, comment les optimiser avec le throttling et le debouncing, et quand contourner complètement JavaScript en utilisant des solutions CSS modernes et des APIs.
Points clés à retenir
- Les événements resize se déclenchent des centaines de fois par seconde lors du redimensionnement de fenêtre, causant de graves problèmes de performance
- Le throttling et le debouncing sont des techniques essentielles pour limiter la fréquence d’exécution des gestionnaires d’événements
- Les alternatives modernes comme l’API ResizeObserver et les requêtes de conteneur CSS offrent souvent de meilleures performances
- Un nettoyage approprié des écouteurs d’événements prévient les fuites mémoire dans les applications de production
Le coût de performance caché des événements resize JavaScript
Quand un utilisateur fait glisser pour redimensionner sa fenêtre de navigateur, l’événement resize ne se déclenche pas une seule fois—il se déclenche continuellement. Un simple glissement de fenêtre peut déclencher le gestionnaire d’événement des centaines de fois par seconde, inondant le thread principal d’appels de fonction.
// Ceci s'affiche des centaines de fois lors d'une seule action de redimensionnement
window.addEventListener('resize', () => {
console.log(`Taille de fenêtre : ${window.innerWidth}x${window.innerHeight}`);
});
Chaque exécution d’événement bloque le thread principal, empêchant le navigateur de gérer d’autres tâches critiques comme les mises à jour de rendu ou le traitement des interactions utilisateur. Le résultat ? Des animations saccadées, des interfaces qui ne répondent pas, et des utilisateurs frustrés.
Pourquoi les pièges de l’événement resize JavaScript importent pour les performances
Déclenchement excessif d’événements et blocage du thread principal
L’événement resize se déclenche pour chaque changement de pixel lors du redimensionnement de fenêtre. Si votre gestionnaire effectue des calculs complexes ou des manipulations DOM, vous exécutez essentiellement des opérations coûteuses des centaines de fois par seconde.
Considérez ce modèle courant :
window.addEventListener('resize', () => {
const elements = document.querySelectorAll('.responsive-element');
elements.forEach(el => {
// Calculs complexes pour chaque élément
el.style.width = calculateOptimalWidth(el);
});
});
Ce code recalcule et met à jour plusieurs éléments continuellement pendant le redimensionnement, créant un goulot d’étranglement de performance.
Layout Thrashing : le tueur de performance silencieux
Le piège le plus insidieux de l’événement resize JavaScript se produit quand vous lisez les dimensions d’éléments et écrivez immédiatement de nouveaux styles. Ce modèle, appelé layout thrashing, force le navigateur à recalculer les layouts de manière synchrone :
window.addEventListener('resize', () => {
// Force le calcul de layout
const width = element.offsetWidth;
// Invalide le layout
element.style.width = (width * 0.8) + 'px';
// Force un autre calcul de layout
const height = element.offsetHeight;
});
Chaque lecture de dimension déclenche un recalcul complet de layout, multiplié par des centaines d’événements resize.
Techniques d’optimisation essentielles : Throttling et Debouncing
Implémentation du throttle pour les événements resize
Le throttling limite la fréquence d’exécution de votre gestionnaire resize, typiquement à 60fps (toutes les 16ms) ou moins :
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(() => {
// Ceci s'exécute au maximum une fois toutes les 100ms
updateLayout();
}, 100);
window.addEventListener('resize', throttledResize);
Discover how at OpenReplay.com.
Debouncing pour les actions de redimensionnement complètes
Le debouncing attend que le redimensionnement s’arrête avant d’exécuter :
function debounce(func, delay) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
const debouncedResize = debounce(() => {
// S'exécute 250ms après l'arrêt du redimensionnement
recalculateLayout();
}, 250);
window.addEventListener('resize', debouncedResize);
// N'oubliez pas le nettoyage
// window.removeEventListener('resize', debouncedResize);
Alternatives modernes aux événements resize de fenêtre
Solutions CSS d’abord : Media Queries et Container Queries
Souvent, vous pouvez éviter complètement les pièges de l’événement resize JavaScript en utilisant CSS :
/* Media queries pour la responsivité basée sur la fenêtre */
@media (max-width: 768px) {
.sidebar { display: none; }
}
/* Container queries pour la responsivité basée sur les composants */
.card-container {
container-type: inline-size;
}
@container (min-width: 400px) {
.card { grid-template-columns: 1fr 1fr; }
}
Pour la détection de media query basée sur JavaScript, utilisez matchMedia :
const mediaQuery = window.matchMedia('(max-width: 768px)');
mediaQuery.addEventListener('change', (e) => {
// Se déclenche seulement lors du franchissement du breakpoint
if (e.matches) {
showMobileMenu();
}
});
API ResizeObserver : l’alternative performante
ResizeObserver fournit une surveillance de taille spécifique aux éléments sans pénalités de performance :
const resizeObserver = new ResizeObserver(entries => {
for (const entry of entries) {
// Le navigateur fournit la taille—pas de reflow forcé
const { width, height } = entry.contentRect;
updateElementLayout(entry.target, width, height);
}
});
resizeObserver.observe(document.querySelector('.responsive-container'));
// Nettoyage quand terminé
// resizeObserver.disconnect();
Bonnes pratiques pour la gestion du resize prête pour la production
- Toujours nettoyer les écouteurs d’événements pour prévenir les fuites mémoire
- Choisir le bon outil : Utilisez CSS pour le styling, ResizeObserver pour la surveillance d’éléments, et les événements resize throttlés seulement quand nécessaire
- Mesurer l’impact sur les performances en utilisant le panneau Performance des Chrome DevTools
- Considérer les écouteurs passifs pour de meilleures performances de défilement quand applicable
Conclusion
En comprenant ces pièges de l’événement resize JavaScript et en implémentant les solutions appropriées, vous pouvez construire des interfaces responsives qui fonctionnent fluidement sur tous les appareils. La clé est de choisir la bonne approche pour votre cas d’usage spécifique—que ce soit des solutions basées sur CSS, des APIs modernes, ou des gestionnaires d’événements correctement optimisés. Commencez par CSS quand possible, utilisez ResizeObserver pour la surveillance spécifique aux éléments, et réservez les événements resize throttlés ou debouncés pour les cas où la surveillance au niveau de la fenêtre est vraiment nécessaire.
FAQ
Le throttling limite l'exécution à un intervalle fixe pendant le redimensionnement continu, s'exécutant régulièrement pendant que l'utilisateur fait glisser. Le debouncing attend que le redimensionnement s'arrête complètement avant de s'exécuter une fois. Utilisez le throttling pour les mises à jour en temps réel et le debouncing pour les calculs finaux.
ResizeObserver a un excellent support dans les navigateurs modernes incluant Chrome, Firefox, Safari, et Edge. Pour les navigateurs plus anciens, utilisez un polyfill ou revenez aux événements resize throttlés avec détection de fonctionnalité pour assurer la compatibilité.
Utilisez les container queries quand le styling dépend de la taille d'un élément plutôt que du viewport. Elles sont parfaites pour les designs basés sur les composants, les layouts de cartes, et la typographie responsive sans surcharge JavaScript ou préoccupations de performance.
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.