Tutorial

How To Use the Resize Observer JavaScript API

JavaScript

Introduction

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.

In this tutorial, you will learn about basic usage of React Observer. You will also implement React Observer in your own front end code and test for browser support.

Prerequisites

To successfully complete this tutorial, you will need the following:

Step 1 — Understanding Basic Usage of Resize Observe

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

const myObserver = new ResizeObserver(entries => {

});

Within the callback function, you might iterate over the entries. With ResizeObserver instantiated, the observe function is called on the instance and the elements to observe are passed in:

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

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

Each entry is assigned 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, the contentRect 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, you want log an observed element’s width and height when the element’s size changes, begin by creating a constant variable called myObserver and instantiating a new ResizeObserver:

const myObserver = new ResizeObserver(entries => {

});

Inside of the callback function, iterate through every entry using forEach:

const myObserver = new ResizeObserver(entries => {
  entries.forEach(entry => {

  });
});

Within the forEach loop, console.log the width and height of each entry using entry.contentRect.width and entry.contentRect.height respectively:

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

To put myObserver to use, create an element called someEl using a DOM selector. Pass in someEl as the argument for myObserver.observe:

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);

With an understanding of how Resize Observer is used, you can now move on to utilizing Resize Observer for a real world use case.

Step 2 — Demonstrating How Resize Observer is Used

Below is a 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:

Before using Resize Observer API, you’ll first need to create an index.html file:

  • touch index.html

Inside of your HTML file, add the following code:

index.html
<div class="box">
  <h3 class="info"></h3>
</div>
<div class="box small">
  <h3 class="info"></h3>
</div>

You’ll also need to add some styles to your HTML. Create a styles.css file and add the following CSS code to that file:

styles.css
.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 the gradient background didn’t need to be applied to the .box element. The resize observer will be called once when the page first loads and the gradient will be applied then.

Now, it’s time to move on to the JavaScript code. You can create an external JavaScript file or you can add <script> tags to your HTML file. First, create a DOM selector for all .box elements:

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

Now instantiate a new ResizeObserver with a callback function that takes a parameter called entries:

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

const myObserver = new ResizeObserver(entries => {

});

Create a for...of loop that will iterate through each entry in entries. Within the loop, create a constant variable infoEl that is set equal to entry.target.querySelector('.info'). This points to the .info element as the target:

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

const myObserver = new ResizeObserver(entries => {
  for (let entry of entries) {
    const infoEl = entry.target.querySelector('.info');
  }
});

Create constant variables width and height which will be set to entry.contentRect.width and entry.contentRect.height respectively. Apply Math.floor to both to round down the values:

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);
  }
});

Since you will be creating a gradient at an angle that changes with the width of your screen, create an angle variable that will be set equal to width / 360 * 100. Again, use Math.floor to round this value down:

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);
  }
});

Create a constant called gradient that will hold the code for a linear gradient:

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), rgba(250,224,66,1))`;
  }
});

With this gradient in place, you will need to set the background of the target entry to gradient using entry.target.style.background:

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), rgba(250,224,66,1))`;

    entry.target.style.background = gradient;
  }
});

It would be helpful to see the values for the width and height on the screen as the change. Take the innerText of infoEl and set it to I'm ${width}px and ${height}px tall:

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), rgba(250,224,66,1))`;

    entry.target.style.background = gradient;

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

The callback for ResizeObserver is complete. Now the myObserver function can be applied to boxes using a forEach loop:

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), rgba(250,224,66,1))`;

    entry.target.style.background = gradient;

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

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

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

This is a working example of how Resize Observer can be used for responsive design. It’s important, though, to be aware of browser support for JavaScript features like this.

Step 3 — Evaluating Browser Support

Browser support for Resize Observer isn’t very extensive right now. Thankfully, there’s a polyfill that can be used 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.

Conclusion

In this tutorial, you were able to understand what Reserve Observer can do, use it in your JavaScript code, and test for browser support.

For next steps, you may be interested in learning how to tackle responsive design with JavaScript frameworks like React and Vue.

Creative Commons License