Quick guide to Resize Observer

murashow

Makar Murashov

Posted on July 4, 2022

Quick guide to Resize Observer

We all try to make our apps and pages to be responsive: this is one of the main web-development standards for years. We strive to support all possible screen sizes while maintaining a friendly user interface. Everyone nowadays used to respond to viewport changes:

  • either use Media queries in CSS @media (mediaQueryString) {...} [MDN] or in JS window.matchMedia(mediaQueryString) [MDN]
  • or listen to window resize via window.addEventListener('resize', ()=>{...})

But what if we need to watch the element size independent of the viewport? Say we have a web-component or autonomous block and want to update it whenever its width or height changes for any reason, something like Element.onResize(doSomething).
Today we are going to learn how to use the Resize Observer API by example.

API

The ResizeObserver interface reports changes to the dimensions of an Element's content or border box, or the bounding box of an SVGElement.

Observation will respond to every change of Element's size and fires if Element is rendered and it’s size is not 0,0 as well as when:

  • Element is inserted/removed from DOM,
  • Element display gets set to none.

Observation will do not fire for:

  • non-replaced inline Elements (span, strong, i, b, em, etc),
  • changes to Element by CSS transforms.

The API provides us two instruments to work with: ResizeObserver and ResizeObserverEntry. Let's talk about them in specific.

ResizeObserver

ResizeObserver is used to observe changes to Element's size. All we need is to create our own instance and pass a callback function that will be fired every time, when the size changes:
(Here and below I will use TypeScript to show the exact types)

const myObserver = new ResizeObserver(
  (entries: ResizeObserverEntry[], observer: ResizeObserver) => {
    for (let entry of entries) {
      // Do something with an entry (see in next section)
    }
});
Enter fullscreen mode Exit fullscreen mode

And then start to observe the desired element:

const myElement = document.getElementById('my-element');
myObserver.observe(myElement);
Enter fullscreen mode Exit fullscreen mode

To stop watching the element we call unobserve() method:

myObserver.unobserve(myElement);
Enter fullscreen mode Exit fullscreen mode

To end observing all elements that were observed before by this instance:

myObserver.disconnect();
Enter fullscreen mode Exit fullscreen mode

Options (optional)

ResizeObserver can observe different kinds of CSS sizes:

  • content-box (default value): size of element's content area,
  • border-box: size of element's box border area (content + padding + border),
  • device-pixel-content-box: size of element's content area in device pixels. Easier to understand it as window.devicePixelRatio * contentSize. Note that due to browser-specific subpixel calculations it's only approximately.

What size to watch can be passed as an option when starting to observe:

interface ResizeObserverOptions {
    box?: 'content-box' | 'border-box' | 'device-pixel-content-box' | undefined;
}
const myOptions: ResizeObserverOptions = {
  box: 'border-box'
};
myObserver.observe(myElement, myOptions);
Enter fullscreen mode Exit fullscreen mode

However it will not affect the value returned by observer's callback, so use it only if you have a reason to.

ResizeObserverEntry

ResizeObserverEntry contains information about the element whose size has changed:

  • target: the Element itself,
  • contentBoxSize: size of content area,
  • borderBoxSize: size of box border area,
  • devicePixelContentBoxSize: size of content area in device pixels,
  • contentRect: the Element's DOMRect, same as if we call Element.getBoundingClientRect() directly.

Note that contentRect was added only due to current compatibility issues, it may be deprecated in next versions of ResizeObserver API. Consider not to use it in production.

Size

Every ...BoxSize property of an Entry represents an array with ResizeObserverSize object with 2 readonly sizes:

interface ResizeObserverSize {
    readonly inlineSize: number;
    readonly blockSize: number;
}
Enter fullscreen mode Exit fullscreen mode

Think about it as width/height element properties, so inlineSize becomes width and blockSize becomes height.

How to use

Usage is pretty easy, say we have a box of strawberries and getting them bigger make us really happy (and vice versa):

<h1>Mood: <span id="mood">😐</span></h1>
<div id="box">
  πŸ“πŸ“πŸ“ 
  πŸ“πŸ“πŸ“ 
  πŸ“πŸ“πŸ“
</div>
<form>
  <label>
    Love amount ❀️: <input id="grower" type="range" value="16" min="8" max="32" step="1">
  </label>
</form>
Enter fullscreen mode Exit fullscreen mode

Let's write some logic to grow our strawberries:

const mood = document.getElementById('mood')
const box = document.getElementById('box')
const grower = document.getElementById('grower')

grower.addEventListener('input', () => {
  box.style.fontSize = grower.value + 'px'; // Give them some love to grow!
})
Enter fullscreen mode Exit fullscreen mode

Now we can use ResizeObserver to change our mood depending on the box size:

const resizeObserver = new ResizeObserver((entries) => {
  for (let entry of entries) {
    const { inlineSize: width } = entry.contentBoxSize[0];
    mood.textContent = width > 90 ? "😊" : width < 50 ? "😒" : "😐";
  }
});

resizeObserver.observe(box);
Enter fullscreen mode Exit fullscreen mode

You can check how it works all together:

Another great example of the ResizeObserver API is scrolling down the chat window when a new message is added. An example can be seen here.

Browser support

Despite the fact that ResizeObserver API is still in Editor’s Draft(still in progress), according to Can I use its global 94.13% support is pretty impressive. There is also a nice and powerful polyfill that allows you to use it in older browsers (even IE 9-10 πŸ™„).

Hope you enjoyed this guide, stay tuned for more.

πŸ’– πŸ’ͺ πŸ™… 🚩
murashow
Makar Murashov

Posted on July 4, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related