Tutorial

A Look at the Resize Observer JavaScript API

JavaScript

While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or edited it to ensure you have an error-free learning experience. It's on our list, and we're working on it! You can help us out by using the "report an issue" button at the bottom of the tutorial.

Resize Observer is a new JavaScript API that’s very similar to other observer APIs like the Intersection Observer API. It allows for elements to be notified when their size changes.

The most frequent reason for an element’s size to change is when the viewport is resized or the device’s direction changes between portrait and landscape. Up until this point, we’ve had to rely on the global window.resize event to listen for resize events and check if certain elements have changed size. This can easily lead to performance problems due to the large amount of triggered event. In other words, using window.resize is often wasteful because it informs us of every viewport size change, not just when an element’s size actually changes.

There’s also another use case for the Resize Observer API that the window’s resize event can’t help us with: when elements are added or removed from the DOM dynamically, influencing the size of the parent element. This is more and more frequent with modern single-page apps.

Basic Usage

Using Resize Observer is as simple as instantiating a new ResizeObserver object and passing-in a callback function that receives the entries that are observed:

const myObserver = new ResizeObserver(entries => {
  // iterate over the entries, do something.
});

Then, we can call observe on our instance and pass-in an element to observe:

const someEl = document.querySelector('.some-element');
const someOtherEl = document.querySelector('.some-other-element');

myObserver.observe(someEl);
myObserver.observe(someOtherEl);

With each entry, we get an object with a contentRect and a target property. The target is the DOM element itself, and contentRect is an object with the following properties: width, height, x, y, top, right, bottom and left.

Unlike with an element’s getBoundingClientRect, contentRect’s values for width and height don’t include padding values. contentRect.top is the element’s top padding and contentRect.left is the element’s left padding.


If, for example, we want log an observed element’s width and height when the element’s size changes, we could do something like this:

const myObserver = new ResizeObserver(entries => {
  entries.forEach(entry => {
    console.log('width', entry.contentRect.width);
    console.log('height', entry.contentRect.height);
  });
});

const someEl = document.querySelector('.some-element');
myObserver.observe(someEl);

Simple Demo

Below is a simple demonstration to see the Resize Observer API in action. Try it out by resizing your browser window and notice how the gradient angle and text content only change when the element’s size is actually affected:


Let’s break down this simple demo. First, we start with some simple markup:

<div class="box">
  <h3 class="info"></h3>
</div>
<div class="box small">
  <h3 class="info"></h3>
</div>

And a touch of styles:

.box {
  text-align: center;
  height: 20vh;
  border-radius: 8px;
  box-shadow: 0 0 4px var(--subtle);

  display: flex;
  justify-content: center;
  align-items: center;
}

.box h3 {
  color: #fff;
  margin: 0;
  font-size: 5vmin;
  text-shadow: 0 0 10px rgba(0,0,0,0.4);
}

.box.small {
  max-width: 550px;
  margin: 1rem auto;
}

Notice how we don’t need to apply our gradient background to the .box element. The resize observer will be called once when the page first loads and our gradient will be applied then.

Now, the magic happens when we add the following JavaScript code:

const boxes = document.querySelectorAll('.box');

const myObserver = new ResizeObserver(entries => {
  for (let entry of entries) {
    const infoEl = entry.target.querySelector('.info');
    const width = Math.floor(entry.contentRect.width);
    const height = Math.floor(entry.contentRect.height);

    const angle = Math.floor(width / 360 * 100);
    const gradient = `linear-gradient(${ angle }deg, rgba(0,143,104,1) 50%, rgba(250,224,66,1) 50%)`;

    entry.target.style.background = gradient;

    infoEl.innerText = `I'm ${ width }px and ${ height }px tall`;
  }
});

boxes.forEach(box => {
  myObserver.observe(box);
});

Here we’re iterating over the entries in the observer’s callback using a for…of loop, but calling forEach on the entries would work just the same.

Notice how we also have to iterate over the elements that we can to observe and call observe on each element.

Browser Support

Browser support right now is pretty bad, with only Chrome 64+ supporting Resize Observer out of the box. Thankfully, there’s a polyfill we can use in the mean time. The polyfill is based on the MutationObserver API.

You can visit Can I Use resizeobserver? to track support for this feature across the major browsers.

Creative Commons License