Smarter Component Rendering with ResizeObserver
A few years ago, I was talking to Paul Irish and a couple other Chrome engineers about issues we were having while developing components. Paul mentioned that I should write about the biggest one. So, I wrote about a Visible State Change for DOM Elements.
Now, Chrome has added support for a new browser API that solves this exact problem! That API is ResizeObserver. We'de like to think our previous blog influenced the support for it.
Problems with Web Component Development
Some controls need to apply logic to update their layout when they become visible or when they are resized. Until recently, there was no way to detect those situations efficiently using JavaScript.
Handling Resizing (Partial Solution)
We dealt with this situation by handling the window’s resize event, which in typical responsive layouts caused the controls to resize.
This is an older API that can handle resizing in any browser.
Control.constructor(host, options, invalidateOnResize) {
…
if (invalidateOnResize) {
window.addEventListener(‘resize’, this._handleResize);
}
…
}
Control._handleResize() {
If (this.sizeNow != this.lastSize) {
this.sizeNow != this.lastSize;
this.invalidate();
}
}
If the control has custom layout logic, the constructor sets invalidateOnResize to true. When mounting, the control adds a handler to the window.resize event. When the user resizes the window and the page layout is updated, the control checks to see if it’s size changed and if so it invalidates itself to update as needed.
Note: that it calls invalidate instead of refresh because invalidate is async/debounced. So, as the user resizes the window the control does not refresh a million times as they move the mouse.
Why Isn't Handling Resizing a Complete Solution?
This is not a complete solution, because it does not handle situations where elements are resized, shown, or hidden in code. The most common example of this are tab controls which show or hide elements in response to user actions. In these situations, the window is not resized and our controls do not get notifications.
ResizeObserver API to the Rescue
The real solution is a new API called ResizeObserver. It just landed in Chrome and Opera.
The ResizeObserver provides a way to observe one of more elements and receive notifications when their content rectangle changes. This includes resizes and visibility changes causes by code or by CSS. It’s a perfect solution for our layout problem.
The latest version of Wijmo 2019 v1 supports ResizeObserver if the browser supports it (Chrome has roughly 65% of browser market share today) and falls back on the original (resize-based) behavior otherwise.
Here is how our new code looks now that we can use ResizeObserver:
Control.constructor(host, options, invalidateOnResize) {
…
if (invalidateOnResize) {
let szo = getResizeObserver();
if (szo) {
szo.observe(host);
} else { // fall back on the original behavior
window.addEventListener(‘resize’, this._handleResize);
}
}
}
getResizeObserver() { // create a single/static ResizeObserver if needed and possible
if (!Control._szObserver && window[‘ResizeObserver’]) {
Control._szObserver = new ResizeObserver(entries => {
entries.forEach(entry => {
let ctl = Control.getControl(entry.target);
ctl._handleResize();
});
});
}
return Control_szObserver;
}
Now, if the control needs custom layout logic, the code calls getResizeObserver() to get/create a static instance of a ResizeObserver. If the browser supports it, the control adds itself to the observer’s list by calling “observe”.
At this point, whenever the host’s bounding rectangle changes, the observer’s callback is triggered and it invokes the sample _handleResize method as before. This happens when the control size changes, and also when its visibility changes (the size becomes zero). Previously this was impo
This works when the window is resized, when the CSS changes, when elements are added/removed from the DOM, etc.
If the ResizeObserver is not supported, the code falls back on the original logic (less than perfect but better than nothing).
Live Demo of ResizeObserver in Wijmo
We put together a little sample that displays whether your browser supports ResizeObserver.
Demo: https://typescript-yez4rv.stackblitz.io
If your browser supports ResizeObserver, you can click the FlexGrid and FlexChart tabs and they just work.
If it doesn't support ResizeObserver, then when clicking the FlexGrid and FlexChart tabs you will see that the grid and chart don't look right.
Browser Support
As of today, this feature is built into Chrome and Opera. It will soon be in Edge as well and can be tested in Edge's developer preview (that is now using chromium).
Between these browsers, this API can be used in about 70% of the browser market. We expect that number to grow very soon when Firefox and Safari implement it too.
ResizeObserver - A Little API with Big Impact
This might seem like an insignificant API, but it really gives us (and you) the ability to build much higher quality components. We can determine exactly when we need to update the layout instead of relying on timers, or customers to write code themselves. Needless to say, we are very happy that this API has been standardized and added to some modern browsers. We hope to see it in Safar and Firefox soon.