ResizeObserver
vous indique quand la taille d'un élément change.
Avant ResizeObserver
, vous deviez associer un écouteur à l'événement resize
du document pour être averti de toute modification des dimensions du viewport. Dans le gestionnaire d'événements, vous devrez ensuite déterminer quels éléments ont été affectés par ce changement et appeler une routine spécifique pour réagir de manière appropriée. Si vous aviez besoin des nouvelles dimensions d'un élément après un redimensionnement, vous deviez appeler getBoundingClientRect()
ou getComputedStyle()
, ce qui peut entraîner un gaspillage de mise en page si vous ne vous occupez pas de regrouper toutes vos lectures et toutes vos écritures.
Cela ne s'applique même pas aux cas où les éléments changent de taille sans que la fenêtre principale n'ait été redimensionnée. Par exemple, l'ajout de nouveaux enfants, la définition du style display
d'un élément sur none
ou des actions similaires peuvent modifier la taille d'un élément, de ses frères et sœurs ou de ses ancêtres.
C'est pourquoi ResizeObserver
est une primitive utile. Elle réagit aux changements de taille des éléments observés, indépendamment de la cause du changement.
Il permet également d'accéder à la nouvelle taille des éléments observés.
API
Toutes les API portant le suffixe Observer
que nous avons mentionnées ci-dessus partagent une conception d'API simple. ResizeObserver
ne fait pas exception. Vous créez un objet ResizeObserver
et transmettez un rappel au constructeur. Le rappel reçoit un tableau d'objets ResizeObserverEntry
(une entrée par élément observé) qui contient les nouvelles dimensions de l'élément.
var ro = new ResizeObserver(entries => {
for (let entry of entries) {
const cr = entry.contentRect;
console.log('Element:', entry.target);
console.log(`Element size: ${cr.width}px x ${cr.height}px`);
console.log(`Element padding: ${cr.top}px ; ${cr.left}px`);
}
});
// Observe one or multiple elements
ro.observe(someElement);
Quelques informations
Quel type de contenu avez-vous signalé ?
En général, un ResizeObserverEntry
indique la zone de contenu d'un élément via une propriété appelée contentRect
, qui renvoie un objet DOMRectReadOnly
. La zone de contenu est la zone dans laquelle le contenu peut être placé. Il s'agit du cadre de bordure moins la marge intérieure.
Il est important de noter que si ResizeObserver
renvoie à la fois les dimensions de contentRect
et la marge intérieure, il regarde uniquement le contentRect
.
Ne confondez pas contentRect
avec le cadre de délimitation de l'élément. Le cadre de délimitation, tel que signalé par getBoundingClientRect()
, est le cadre qui contient l'élément entier et ses descendants. Les fichiers SVG constituent une exception à la règle, où ResizeObserver
indique les dimensions du cadre de délimitation.
Depuis Chrome 84, ResizeObserverEntry
dispose de trois nouvelles propriétés pour fournir des informations plus détaillées. Chacune de ces propriétés renvoie un objet ResizeObserverSize
contenant une propriété blockSize
et une propriété inlineSize
. Ces informations concernent l'élément observé au moment de l'appel du rappel.
borderBoxSize
contentBoxSize
devicePixelContentBoxSize
Tous ces éléments renvoient des tableaux en lecture seule, car ils devraient, à l'avenir, prendre en charge les éléments comportant plusieurs fragments, qui se produisent dans des scénarios multicolonnes. Pour l'instant, ces tableaux ne contiennent qu'un seul élément.
La compatibilité de la plate-forme avec ces propriétés est limitée, mais Firefox est déjà compatible avec les deux premières.
Quand le problème est-il signalé ?
La spécification interdit à ResizeObserver
de traiter tous les événements de redimensionnement avant la peinture et après la mise en page. Le rappel d'un ResizeObserver
est donc l'endroit idéal pour apporter des modifications à la mise en page de votre page. Étant donné que le traitement ResizeObserver
se produit entre la mise en page et la peinture, cela n'invalide que la mise en page, et non la peinture.
J'ai compris
Vous vous demandez peut-être ce qui se passe si je remplace la taille d'un élément observé dans le rappel par ResizeObserver
? La réponse est la suivante : vous déclencherez immédiatement un autre appel au rappel. Heureusement, ResizeObserver
dispose d'un mécanisme permettant d'éviter les boucles de rappel infinies et les dépendances cycliques. Les modifications ne seront traitées dans le même frame que si l'élément redimensionné est plus profond dans l'arborescence DOM que l'élément le moins profond traité dans le rappel précédent.
Sinon, elles seront reportées au frame suivant.
Application
ResizeObserver
vous permet d'implémenter des requêtes multimédias par élément. En observant les éléments, vous pouvez définir de manière impérative vos points d'arrêt de conception et modifier les styles d'un élément. Dans l'exemple suivant, le rayon de la bordure de la deuxième zone change en fonction de sa largeur.
const ro = new ResizeObserver(entries => {
for (let entry of entries) {
entry.target.style.borderRadius =
Math.max(0, 250 - entry.contentRect.width) + 'px';
}
});
// Only observe the second box
ro.observe(document.querySelector('.box:nth-child(2)'));
Un autre exemple intéressant est une fenêtre de chat. Le problème qui se pose dans une mise en page de conversation classique de haut en bas est le positionnement du défilement. Pour éviter de dérouter l'utilisateur, il est utile que la fenêtre reste en bas de la conversation, où les messages les plus récents s'affichent. De plus, tout type de changement de mise en page (par exemple, un téléphone passant du mode paysage au mode portrait ou inversement) devrait produire le même résultat.
ResizeObserver
vous permet d'écrire un seul morceau de code qui gère les deux scénarios. Le redimensionnement de la fenêtre est un événement qu'un ResizeObserver
peut capturer par définition, mais l'appel de appendChild()
redimensionne également cet élément (sauf si overflow: hidden
est défini), car il doit libérer de l'espace pour les nouveaux éléments. En gardant cela à l'esprit, il suffit de très peu de lignes pour obtenir l'effet souhaité :
const ro = new ResizeObserver(entries => {
document.scrollingElement.scrollTop =
document.scrollingElement.scrollHeight;
});
// Observe the scrollingElement for when the window gets resized
ro.observe(document.scrollingElement);
// Observe the timeline to process new messages
ro.observe(timeline);
Plutôt sympa, non ?
À partir de là, je pourrais ajouter plus de code pour gérer le cas où l'utilisateur a fait défiler la page manuellement vers le haut et souhaite que le défilement s'en tenir à ce message lorsqu'un nouveau message arrive.
Un autre cas d'utilisation concerne tout type d'élément personnalisé qui effectue sa propre mise en page.
Avant le ResizeObserver
, il n'existait aucun moyen fiable d'être averti lorsque ses dimensions changent afin que ses enfants puissent être disposés à nouveau.
Effets sur l'interaction à la prochaine peinture (INP)
Interaction to Next Paint (INP) est une métrique qui mesure la réactivité globale d'une page aux interactions des utilisateurs. Si l'INP d'une page se situe dans le seuil "bon" (c'est-à-dire 200 millisecondes ou moins), on peut dire qu'elle répond de manière fiable aux interactions de l'utilisateur avec elle.
Bien que le temps nécessaire à l'exécution des rappels d'événements en réponse à une interaction utilisateur puisse contribuer de manière significative à la latence totale d'une interaction, ce n'est pas le seul aspect de l'INP à prendre en compte. L'INP tient également compte du temps nécessaire pour que la peinture suivante de l'interaction se produise. Il s'agit du temps nécessaire pour que le travail de rendu requis pour mettre à jour l'interface utilisateur en réponse à une interaction soit terminé.
Pour ResizeObserver
, cela est important, car le rappel exécuté par une instance ResizerObserver
se produit juste avant le travail de rendu. Il s'agit d'une conception, car le travail qui se produit dans le rappel doit être pris en compte, car le résultat de ce travail nécessitera très probablement une modification de l'interface utilisateur.
Veillez à effectuer le moins de travail de rendu que possible dans un rappel ResizeObserver
, car un travail de rendu excessif peut créer des situations où le navigateur est retardé dans l'exécution de tâches importantes. Par exemple, si une interaction comporte un rappel qui déclenche l'exécution d'un rappel ResizeObserver
, assurez-vous de procéder comme suit pour faciliter l'expérience la plus fluide possible :
- Assurez-vous que vos sélecteurs CSS sont aussi simples que possible afin d'éviter un trop grand nombre de recalculs de style. Les recalculs de style se produisent juste avant la mise en page, et les sélecteurs CSS complexes peuvent retarder les opérations de mise en page.
- Évitez d'effectuer des tâches dans votre rappel
ResizeObserver
qui peuvent déclencher des reflows forcés. - Le temps nécessaire pour mettre à jour la mise en page d'une page augmente généralement avec le nombre d'éléments DOM sur la page. Bien que cela soit vrai que les pages utilisent ou non
ResizeObserver
, le travail effectué dans un rappelResizeObserver
peut devenir important à mesure que la complexité structurelle d'une page augmente.
Conclusion
ResizeObserver
est disponible dans tous les principaux navigateurs et permet de surveiller efficacement le redimensionnement des éléments au niveau de l'élément. Veillez simplement à ne pas trop retarder l'affichage avec cette API puissante.