or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-resize-observer-polyfill

A polyfill for the Resize Observer API that enables developers to observe changes to the dimensions of DOM elements

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/resize-observer-polyfill@1.5.x

To install, run

npx @tessl/cli install tessl/npm-resize-observer-polyfill@1.5.0

index.mddocs/

Resize Observer Polyfill

A 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.

Package Information

  • Package Name: resize-observer-polyfill
  • Package Type: npm
  • Language: JavaScript/TypeScript
  • Installation: npm install resize-observer-polyfill

Core Imports

import 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 -->

Basic Usage

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();

Architecture

The polyfill implements the official ResizeObserver specification and follows these key principles:

  • Ponyfill Design: Doesn't modify global objects unless needed
  • Fallback Strategy: Uses MutationObserver primarily, with Mutation Events fallback for IE9-10
  • Performance Optimization: Batches and throttles updates with ~20ms delay
  • Spec Compliance: Follows the W3C ResizeObserver specification exactly
  • Browser Compatibility: Supports IE9+ through modern browsers

Capabilities

ResizeObserver Constructor

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);
  });
});

Element Observation

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();

ResizeObserver Entry Data

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}`);
  });
});

Advanced Usage Patterns

Responsive Component Behavior

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();
  }
}

Container Query Simulation

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;
}

Browser Support and Limitations

Supported Browsers

  • Chrome, Firefox, Safari, Edge (modern versions)
  • Internet Explorer 9+
  • Mobile browsers (iOS Safari, Chrome Mobile, etc.)

Known Limitations

  • Notifications are delivered ~20ms after actual changes happen
  • Changes caused by dynamic pseudo-classes (:hover, :focus) are not tracked
  • Delayed transitions receive only one notification with final dimensions

Workaround for Pseudo-class Changes

// Add a short transition to trigger transitionend events
.element:hover {
  transition: opacity 0.001s;
}

TypeScript Support

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}`);
    });
  }
);

Error Handling

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."
}