The ResizeObserver
interface reports changes to the dimensions of an Element
's content or border box, or the bounding box of an SVGElement
.
Note: The content box is the box in which content can be placed, meaning the border box minus the padding and border width. The border box encompasses the content, padding, and border. See The box model for further explanation.
In the resize-observer-text.html (see source) example, we use the resize observer to change the font-size
of a header and paragraph as a slider's value is changed causing the containing <div>
to change width. This shows that you can respond to changes in an element's size, even if they have nothing to do with the viewport.
We also provide a checkbox to turn the observer off and on. If it is turned off, the text will not change in response to the <div>
's width changing.
The JavaScript looks like so:
const h1Elem = document.querySelector("h1");
const pElem = document.querySelector("p");
const divElem = document.querySelector("body > div");
const slider = document.querySelector('input[type="range"]');
const checkbox = document.querySelector('input[type="checkbox"]');
divElem.style.width = "600px";
slider.addEventListener("input", () => {
divElem.style.width = `${slider.value}px`;
});
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
if (entry.contentBoxSize) {
const contentBoxSize = entry.contentBoxSize[0];
h1Elem.style.fontSize = `${Math.max(
1.5,
contentBoxSize.inlineSize / 200,
)}rem`;
pElem.style.fontSize = `${Math.max(
1,
contentBoxSize.inlineSize / 600,
)}rem`;
} else {
h1Elem.style.fontSize = `${Math.max(
1.5,
entry.contentRect.width / 200,
)}rem`;
pElem.style.fontSize = `${Math.max(1, entry.contentRect.width / 600)}rem`;
}
}
console.log("Size changed");
});
resizeObserver.observe(divElem);
checkbox.addEventListener("change", () => {
if (checkbox.checked) {
resizeObserver.observe(divElem);
} else {
resizeObserver.unobserve(divElem);
}
});
Implementations following the specification invoke resize events before paint (that is, before the frame is presented to the user). If there was any resize event, style and layout are re-evaluated — which in turn may trigger more resize events. Infinite loops from cyclic dependencies are addressed by only processing elements deeper in the DOM during each iteration. Resize events that don't meet that condition are deferred to the next paint, and an error event is fired on the Window
object, with the well-defined message string:
ResizeObserver loop completed with undelivered notifications.
Note that this only prevents user-agent lockup, not the infinite loop itself. For example, the following code will cause the width of divElem
to grow indefinitely, with the above error message in the console repeating every frame:
const divElem = document.querySelector("body > div");
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
entry.target.style.width = entry.contentBoxSize[0].inlineSize + 10 + "px";
}
});
window.addEventListener("error", function (e) {
console.error(e.message);
});
As long as the error event does not fire indefinitely, resize observer will settle and produce a stable, likely correct, layout. However, visitors may see a flash of broken layout, as a sequence of changes expected to happen in a single frame is instead happening over multiple frames.