A polyfill for the Resize Observer API that enables developers to observe changes to the dimensions of DOM elements
npx @tessl/cli install tessl/npm-resize-observer-polyfill@1.5.0A polyfill for the Resize Observer API that enables developers to observe changes to the dimensions of DOM elements. This polyfill uses MutationObserver as the primary strategy with Mutation Events as a fallback for older browsers, avoiding polling mechanisms while maintaining performance.
npm install resize-observer-polyfillimport ResizeObserver from 'resize-observer-polyfill';For CommonJS:
const ResizeObserver = require('resize-observer-polyfill');For UMD (browser globals):
<script src="./node_modules/resize-observer-polyfill/dist/ResizeObserver.js"></script>
<!-- ResizeObserver is now available globally -->import ResizeObserver from 'resize-observer-polyfill';
// Create a ResizeObserver instance
const resizeObserver = new ResizeObserver((entries, observer) => {
for (const entry of entries) {
const { left, top, width, height } = entry.contentRect;
console.log('Element:', entry.target);
console.log(`Element's size: ${width}px x ${height}px`);
console.log(`Element's paddings: ${top}px ; ${left}px`);
}
});
// Start observing an element
resizeObserver.observe(document.body);
// Stop observing a specific element
resizeObserver.unobserve(document.body);
// Stop observing all elements
resizeObserver.disconnect();The polyfill implements the official ResizeObserver specification and follows these key principles:
Creates a new ResizeObserver instance that can observe element size changes.
/**
* Creates a new ResizeObserver instance
* @param callback - Function called when observed elements change size
*/
constructor(callback: ResizeObserverCallback): ResizeObserver;
interface ResizeObserverCallback {
(entries: ResizeObserverEntry[], observer: ResizeObserver): void;
}Usage Example:
const observer = new ResizeObserver((entries, observer) => {
entries.forEach(entry => {
console.log('Size changed:', entry.contentRect);
});
});Methods for managing which elements are being observed for size changes.
/**
* Begin observing size changes for the specified element
* @param target - DOM element to observe
*/
observe(target: Element): void;
/**
* Stop observing size changes for the specified element
* @param target - DOM element to stop observing
*/
unobserve(target: Element): void;
/**
* Stop observing all currently observed elements
*/
disconnect(): void;Usage Examples:
// Start observing multiple elements
observer.observe(document.getElementById('sidebar'));
observer.observe(document.querySelector('.main-content'));
// Stop observing a specific element
observer.unobserve(document.getElementById('sidebar'));
// Stop all observations
observer.disconnect();The data structure provided to the callback function for each observed element.
interface ResizeObserverEntry {
/** The observed element that changed size */
readonly target: Element;
/** The element's content rectangle with size information */
readonly contentRect: DOMRectReadOnly;
}
interface DOMRectReadOnly {
/** X coordinate of the rectangle */
readonly x: number;
/** Y coordinate of the rectangle */
readonly y: number;
/** Width of the rectangle */
readonly width: number;
/** Height of the rectangle */
readonly height: number;
/** Distance from top edge to element's top padding edge */
readonly top: number;
/** Distance from left edge to element's right padding edge */
readonly right: number;
/** Distance from top edge to element's bottom padding edge */
readonly bottom: number;
/** Distance from left edge to element's left padding edge */
readonly left: number;
}Usage Example:
const observer = new ResizeObserver((entries) => {
entries.forEach(entry => {
const { target, contentRect } = entry;
const { width, height, top, left } = contentRect;
// Update element based on its new size
if (width < 300) {
target.classList.add('compact');
} else {
target.classList.remove('compact');
}
console.log(`${target.tagName} is now ${width}x${height}`);
});
});import ResizeObserver from 'resize-observer-polyfill';
class ResponsiveComponent {
constructor(element) {
this.element = element;
this.observer = new ResizeObserver(this.handleResize.bind(this));
this.observer.observe(element);
}
handleResize(entries) {
const entry = entries[0];
const { width } = entry.contentRect;
// Apply different layouts based on width
if (width < 480) {
this.element.className = 'component mobile';
} else if (width < 768) {
this.element.className = 'component tablet';
} else {
this.element.className = 'component desktop';
}
}
destroy() {
this.observer.disconnect();
}
}import ResizeObserver from 'resize-observer-polyfill';
function setupContainerQueries() {
const containers = document.querySelectorAll('[data-container-query]');
const observer = new ResizeObserver((entries) => {
entries.forEach(entry => {
const { target, contentRect } = entry;
const { width } = contentRect;
// Remove existing size classes
target.classList.remove('sm', 'md', 'lg', 'xl');
// Add appropriate size class
if (width >= 1200) {
target.classList.add('xl');
} else if (width >= 992) {
target.classList.add('lg');
} else if (width >= 768) {
target.classList.add('md');
} else {
target.classList.add('sm');
}
});
});
containers.forEach(container => observer.observe(container));
return observer;
}:hover, :focus) are not tracked// Add a short transition to trigger transitionend events
.element:hover {
transition: opacity 0.001s;
}The package includes full TypeScript definitions:
import ResizeObserver from 'resize-observer-polyfill';
const observer: ResizeObserver = new ResizeObserver(
(entries: ResizeObserverEntry[], observer: ResizeObserver) => {
entries.forEach((entry: ResizeObserverEntry) => {
const rect: DOMRectReadOnly = entry.contentRect;
console.log(`Size: ${rect.width}x${rect.height}`);
});
}
);The ResizeObserver follows the same error handling as the native implementation:
const observer = new ResizeObserver((entries) => {
// Callback errors don't break the observer
entries.forEach(entry => {
try {
processEntry(entry);
} catch (error) {
console.error('Error processing resize entry:', error);
}
});
});
// Constructor validates arguments
try {
new ResizeObserver(); // Throws TypeError
} catch (error) {
console.error(error.message); // "1 argument required, but only 0 present."
}
try {
new ResizeObserver.call(null, () => {}); // Throws TypeError
} catch (error) {
console.error(error.message); // "Cannot call a class as a function."
}