ResizeObserver: il s'apparente à document.onresize pour les éléments.

ResizeObserver vous indique lorsque 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 de la fenêtre d'affichage. Dans le gestionnaire d'événements, vous devez ensuite identifier les éléments affectés par cette modification 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 thrashing de la mise en page si vous ne vous occupez pas de traiter toutes vos lectures et toutes vos écritures.

Cela ne concernait même pas les cas où les éléments changent de taille sans que la fenêtre principale 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 de n'importe quel élément observé, indépendamment de la cause du changement. Elle permet également d'accéder à la nouvelle taille des éléments observés.

Navigateurs pris en charge

  • 64
  • 79
  • 69
  • 13.1

Source

API

Toutes les API avec 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 détails

Quels sont les éléments signalés ?

En règle générale, une ResizeObserverEntry signale 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 de la zone de bordure moins la marge intérieure.

Schéma du modèle de zone CSS.

Notez que si ResizeObserver indique à la fois les dimensions de l'élément contentRect et la marge intérieure, il surveille uniquement l'élément contentRect. Ne confondez pas contentRect avec le cadre de délimitation de l'élément. Comme indiqué par getBoundingClientRect(), le cadre de délimitation contient l'intégralité de l'élément et ses descendants. Les SVG font 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 permettant de 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 à l'avenir, nous espérons qu'ils pourront prendre en charge des é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é des plates-formes avec ces propriétés est limitée, mais Firefox prend déjà en charge les deux premières.

Quand est-il signalé ?

La spécification stipule que ResizeObserver doit traiter tous les événements de redimensionnement avant l'opération "Paint" et après la mise en page. Ainsi, le rappel d'un ResizeObserver est l'endroit idéal pour modifier la mise en page de votre page. Étant donné que le traitement de ResizeObserver s'effectue entre la mise en page et l'objet Paint, cela n'invalide que la mise en page, et non le rendu.

J'ai compris

Vous vous demandez peut-être que 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 allez immédiatement déclencher un autre appel du 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 la même image que si l'élément redimensionné est plus profond dans l'arborescence DOM que l'élément le plus superficiel traité dans le rappel précédent. Sinon, elles sont reportées à l'image suivante.

application

L'une des actions que ResizeObserver vous permet de faire consiste à 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, l'arrondi de la bordure de la deuxième zone est modifié 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)'));

Autre exemple intéressant : la fenêtre de chat. Le problème qui se pose dans une mise en page classique de haut en bas est le positionnement du défilement. Pour éviter de perturber l'utilisateur, il est utile que la fenêtre reste en bas de la conversation, où les messages les plus récents apparaissent. De plus, tout changement de mise en page (comme si vous passez du mode paysage au mode portrait ou inversement) devrait parvenir au même résultat.

ResizeObserver vous permet d'écrire un seul élément 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 faire de la place pour les nouveaux éléments. En gardant cela à l'esprit, très peu de lignes sont nécessaires 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 du code pour gérer le cas où l'utilisateur a fait défiler l'écran vers le haut manuellement et souhaite que le défilement reste ce message lorsqu'un nouveau message arrive.

Autre cas d'utilisation : tout type d'élément personnalisé qui effectue sa propre mise en page. Jusqu'au ResizeObserver, il n'y avait aucun moyen fiable d'être averti lorsque ses dimensions changent afin que ses enfants puissent être à nouveau disposés.

Effets sur l'interaction to Next Paint (INP)

INP (Interaction to Next Paint) 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'une page 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. INP tient également compte du temps nécessaire pour que le prochain peinture de l'interaction se produise. Il s'agit du temps nécessaire pour que le travail de rendu soit nécessaire pour mettre à jour l'interface utilisateur en réponse à une interaction.

En ce qui concerne ResizeObserver, cela est important, car le rappel qu'une instance ResizerObserver est exécuté juste avant le rendu de l'opération. C'est par nature, car le travail qui se produit dans le rappel doit être pris en compte. Le résultat de ce travail nécessitera très probablement une modification de l'interface utilisateur.

Effectuez le moins de tâches de rendu requises dans un rappel ResizeObserver, car un rendu excessif peut retarder le navigateur dans ses tâches importantes. Par exemple, si une interaction comporte un rappel qui entraîne l'exécution d'un rappel ResizeObserver, assurez-vous de procéder comme suit pour faciliter l'expérience:

  • Assurez-vous que vos sélecteurs CSS sont aussi simples que possible afin d'éviter un trop grand nombre de tâches de recalcul du style. Les recalculs de style ont lieu juste avant la mise en page, et des sélecteurs CSS complexes peuvent retarder les opérations de mise en page.
  • Évitez d'effectuer toute tâche dans votre rappel ResizeObserver qui pourrait déclencher des ajustements de la mise en page 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 rappel ResizeObserver peut devenir important à mesure que la complexité structure d'une page augmente.

Conclusion

ResizeObserver est disponible dans tous les principaux navigateurs et offre un moyen efficace de surveiller les redimensionnements d'éléments au niveau d'un élément. Faites attention à ne pas trop retarder l'affichage avec cette API performante.