DEV Community

Cover image for Quick guide to Resize Observer
Makar Murashov
Makar Murashov

Posted on • Updated on

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.

Top comments (4)

Collapse
 
iamhectorsosa profile image
Hector Sosa

Very similar structure to Intersection Observer API. Great article, Makar! Cheers!

Collapse
 
murashow profile image
Makar Murashov

Thanks Hector, I like it a lot, it really quite easy to use and very useful as well

Collapse
 
rainxh11 profile image
Ahmed Chakhoum

In a true W3C Fashion, a confusing API that only work 30% of the time
IMO they should just scrap HTML & CSS altogether and start over with a proper rendering API and Markup language
I've spent days trying to make something working in a WYSIWYG component and make it react to zoom & resize changes with no success
a very stupid thing that takes me 5 minutes to accomplish in Flutter or Kotlin Jetpack or with any other Sane UI Rendering APIs

Collapse
 
san2008prog profile image
san2008-prog

Hello, one question please. What it means the Zero in:
const { inlineSize: width } = entry.contentBoxSize[0]

And how it is related with the others elements in the example. I mean zero is the first element in the array, but in which cases it would one or two, etc.

Thanks.