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:
- An understanding of HTML. The How To Build a Website with HTML series is a great place to start.
- A basic knowledge of CSS. The How To Build a Website With CSS series can help you to achieve this.
- An understanding of JavaScript
for
andforEach
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 howforEach
loops work. - An understanding of DOM Manipulation in JavaScript. Visit the Understanding the DOM — Document Object Model Series if you want to learn more about this topic.
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 => {
});
Copy
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);
Copy
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 => {
});
Copy
Inside of the callback function, iterate through every entry using forEach
:
const myObserver = new ResizeObserver(entries => {
entries.forEach(entry => {
});
});
Copy
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);
});
});
Copy
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);
Copy
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
Copy
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>
Copy
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;
}
Copy
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');
Copy
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 => {
});
Copy
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');
}
});
Copy
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);
}
});
Copy
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);
}
});
Copy
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))`;
}
});
Copy
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;
}
});
Copy
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`;
}
});
Copy
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);
});
Copy
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.