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.
To successfully complete this tutorial, you will need the following:
for
and forEach
loops. To learn more about loops, visit this article called For Loops, For…Of Loops and For…In Loops in JavaScript. This article gives a thorough explanation of how forEach
loops work.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.
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:
<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:
.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.
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.
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.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
I’ve found an error, or not completely accurate statement in your article. When you state in the section “Basic Usage” the following: “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”, you are leading the reader to get the impression that the callback function will receive an “entry” for each of the elements that are been observed. That’s wrong on two aspects: 1- The entries received by the callback are not the elements that are been observed, but a collection of ResizeObserverEntry objects with size information. 2- On observer trigger the callback does not receive an “entry” for each observed element, but an “entry” for each observed element that has change it’s size. So, if you are observing elements: <p id=“p1”></p>, <p id=“p2”></p> ad <p id=“p3”></p>, and only the first element change it’s size, the callback will receive only a ResizeObserverEntry reflecting the changes in the first element.