Quick guide to Resize Observer
Tags: javascript, css, beginners, webdev
Published: 2022-07-04 22:43:53

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](https://developer.mozilla.org/en-US/docs/Web/CSS/@media)] or in JS `window.matchMedia(mediaQueryString)` [[MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia)]
- 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](https://developer.mozilla.org/en-US/docs/Web/API/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)_
```typescript
const myObserver = new ResizeObserver(
  (entries: ResizeObserverEntry[], observer: ResizeObserver) => {
    for (let entry of entries) {
      // Do something with an entry (see in next section)
    }
});
```
And then start to observe the desired element:
```typescript
const myElement = document.getElementById('my-element');
myObserver.observe(myElement);
```
To stop watching the element we call `unobserve()` method:
```typescript
myObserver.unobserve(myElement);
```
To end observing **all** elements that were observed before by this instance:
```typescript
myObserver.disconnect();
```

#### 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:
```typescript
interface ResizeObserverOptions {
    box?: 'content-box' | 'border-box' | 'device-pixel-content-box' | undefined;
}
const myOptions: ResizeObserverOptions = {
  box: 'border-box'
};
myObserver.observe(myElement, myOptions);
```
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](https://developer.mozilla.org/en-US/docs/Web/API/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:
```typescript
interface ResizeObserverSize {
    readonly inlineSize: number;
    readonly blockSize: number;
}
```
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):

```html
<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>
```
Let's write some logic to grow our strawberries:
```javascript
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!
})
```
Now we can use ResizeObserver to change our mood depending on the box size:
```javascript
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);
```

You can check how it works all together:
{% embed https://codesandbox.io/embed/quick-guide-to-resizeobserver-gkwy07?hidenavigation=1&view=preview %}

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](https://rawgit.com/WICG/ResizeObserver/master/examples/chat.html).

## Browser support
Despite the fact that ResizeObserver API is still in _Editor’s Draft_(still in progress), according to [Can I use](https://caniuse.com/?search=resizeobserver) its global `94.13%` support is pretty impressive. There is also a nice and powerful [polyfill](https://github.com/juggle/resize-observer) that allows you to use it in older browsers (even IE 9-10 🙄).

Hope you enjoyed this guide, stay tuned for more.