0
# Resize Observer Polyfill
1
2
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.
3
4
## Package Information
5
6
- **Package Name**: resize-observer-polyfill
7
- **Package Type**: npm
8
- **Language**: JavaScript/TypeScript
9
- **Installation**: `npm install resize-observer-polyfill`
10
11
## Core Imports
12
13
```javascript
14
import ResizeObserver from 'resize-observer-polyfill';
15
```
16
17
For CommonJS:
18
19
```javascript
20
const ResizeObserver = require('resize-observer-polyfill');
21
```
22
23
For UMD (browser globals):
24
25
```html
26
<script src="./node_modules/resize-observer-polyfill/dist/ResizeObserver.js"></script>
27
<!-- ResizeObserver is now available globally -->
28
```
29
30
## Basic Usage
31
32
```javascript
33
import ResizeObserver from 'resize-observer-polyfill';
34
35
// Create a ResizeObserver instance
36
const resizeObserver = new ResizeObserver((entries, observer) => {
37
for (const entry of entries) {
38
const { left, top, width, height } = entry.contentRect;
39
40
console.log('Element:', entry.target);
41
console.log(`Element's size: ${width}px x ${height}px`);
42
console.log(`Element's paddings: ${top}px ; ${left}px`);
43
}
44
});
45
46
// Start observing an element
47
resizeObserver.observe(document.body);
48
49
// Stop observing a specific element
50
resizeObserver.unobserve(document.body);
51
52
// Stop observing all elements
53
resizeObserver.disconnect();
54
```
55
56
## Architecture
57
58
The polyfill implements the official ResizeObserver specification and follows these key principles:
59
60
- **Ponyfill Design**: Doesn't modify global objects unless needed
61
- **Fallback Strategy**: Uses MutationObserver primarily, with Mutation Events fallback for IE9-10
62
- **Performance Optimization**: Batches and throttles updates with ~20ms delay
63
- **Spec Compliance**: Follows the W3C ResizeObserver specification exactly
64
- **Browser Compatibility**: Supports IE9+ through modern browsers
65
66
## Capabilities
67
68
### ResizeObserver Constructor
69
70
Creates a new ResizeObserver instance that can observe element size changes.
71
72
```typescript { .api }
73
/**
74
* Creates a new ResizeObserver instance
75
* @param callback - Function called when observed elements change size
76
*/
77
constructor(callback: ResizeObserverCallback): ResizeObserver;
78
79
interface ResizeObserverCallback {
80
(entries: ResizeObserverEntry[], observer: ResizeObserver): void;
81
}
82
```
83
84
**Usage Example:**
85
86
```javascript
87
const observer = new ResizeObserver((entries, observer) => {
88
entries.forEach(entry => {
89
console.log('Size changed:', entry.contentRect);
90
});
91
});
92
```
93
94
### Element Observation
95
96
Methods for managing which elements are being observed for size changes.
97
98
```typescript { .api }
99
/**
100
* Begin observing size changes for the specified element
101
* @param target - DOM element to observe
102
*/
103
observe(target: Element): void;
104
105
/**
106
* Stop observing size changes for the specified element
107
* @param target - DOM element to stop observing
108
*/
109
unobserve(target: Element): void;
110
111
/**
112
* Stop observing all currently observed elements
113
*/
114
disconnect(): void;
115
```
116
117
**Usage Examples:**
118
119
```javascript
120
// Start observing multiple elements
121
observer.observe(document.getElementById('sidebar'));
122
observer.observe(document.querySelector('.main-content'));
123
124
// Stop observing a specific element
125
observer.unobserve(document.getElementById('sidebar'));
126
127
// Stop all observations
128
observer.disconnect();
129
```
130
131
### ResizeObserver Entry Data
132
133
The data structure provided to the callback function for each observed element.
134
135
```typescript { .api }
136
interface ResizeObserverEntry {
137
/** The observed element that changed size */
138
readonly target: Element;
139
/** The element's content rectangle with size information */
140
readonly contentRect: DOMRectReadOnly;
141
}
142
143
interface DOMRectReadOnly {
144
/** X coordinate of the rectangle */
145
readonly x: number;
146
/** Y coordinate of the rectangle */
147
readonly y: number;
148
/** Width of the rectangle */
149
readonly width: number;
150
/** Height of the rectangle */
151
readonly height: number;
152
/** Distance from top edge to element's top padding edge */
153
readonly top: number;
154
/** Distance from left edge to element's right padding edge */
155
readonly right: number;
156
/** Distance from top edge to element's bottom padding edge */
157
readonly bottom: number;
158
/** Distance from left edge to element's left padding edge */
159
readonly left: number;
160
}
161
```
162
163
**Usage Example:**
164
165
```javascript
166
const observer = new ResizeObserver((entries) => {
167
entries.forEach(entry => {
168
const { target, contentRect } = entry;
169
const { width, height, top, left } = contentRect;
170
171
// Update element based on its new size
172
if (width < 300) {
173
target.classList.add('compact');
174
} else {
175
target.classList.remove('compact');
176
}
177
178
console.log(`${target.tagName} is now ${width}x${height}`);
179
});
180
});
181
```
182
183
## Advanced Usage Patterns
184
185
### Responsive Component Behavior
186
187
```javascript
188
import ResizeObserver from 'resize-observer-polyfill';
189
190
class ResponsiveComponent {
191
constructor(element) {
192
this.element = element;
193
this.observer = new ResizeObserver(this.handleResize.bind(this));
194
this.observer.observe(element);
195
}
196
197
handleResize(entries) {
198
const entry = entries[0];
199
const { width } = entry.contentRect;
200
201
// Apply different layouts based on width
202
if (width < 480) {
203
this.element.className = 'component mobile';
204
} else if (width < 768) {
205
this.element.className = 'component tablet';
206
} else {
207
this.element.className = 'component desktop';
208
}
209
}
210
211
destroy() {
212
this.observer.disconnect();
213
}
214
}
215
```
216
217
### Container Query Simulation
218
219
```javascript
220
import ResizeObserver from 'resize-observer-polyfill';
221
222
function setupContainerQueries() {
223
const containers = document.querySelectorAll('[data-container-query]');
224
225
const observer = new ResizeObserver((entries) => {
226
entries.forEach(entry => {
227
const { target, contentRect } = entry;
228
const { width } = contentRect;
229
230
// Remove existing size classes
231
target.classList.remove('sm', 'md', 'lg', 'xl');
232
233
// Add appropriate size class
234
if (width >= 1200) {
235
target.classList.add('xl');
236
} else if (width >= 992) {
237
target.classList.add('lg');
238
} else if (width >= 768) {
239
target.classList.add('md');
240
} else {
241
target.classList.add('sm');
242
}
243
});
244
});
245
246
containers.forEach(container => observer.observe(container));
247
248
return observer;
249
}
250
```
251
252
## Browser Support and Limitations
253
254
### Supported Browsers
255
- Chrome, Firefox, Safari, Edge (modern versions)
256
- Internet Explorer 9+
257
- Mobile browsers (iOS Safari, Chrome Mobile, etc.)
258
259
### Known Limitations
260
- Notifications are delivered ~20ms after actual changes happen
261
- Changes caused by dynamic pseudo-classes (`:hover`, `:focus`) are not tracked
262
- Delayed transitions receive only one notification with final dimensions
263
264
### Workaround for Pseudo-class Changes
265
266
```javascript
267
// Add a short transition to trigger transitionend events
268
.element:hover {
269
transition: opacity 0.001s;
270
}
271
```
272
273
## TypeScript Support
274
275
The package includes full TypeScript definitions:
276
277
```typescript
278
import ResizeObserver from 'resize-observer-polyfill';
279
280
const observer: ResizeObserver = new ResizeObserver(
281
(entries: ResizeObserverEntry[], observer: ResizeObserver) => {
282
entries.forEach((entry: ResizeObserverEntry) => {
283
const rect: DOMRectReadOnly = entry.contentRect;
284
console.log(`Size: ${rect.width}x${rect.height}`);
285
});
286
}
287
);
288
```
289
290
## Error Handling
291
292
The ResizeObserver follows the same error handling as the native implementation:
293
294
```javascript
295
const observer = new ResizeObserver((entries) => {
296
// Callback errors don't break the observer
297
entries.forEach(entry => {
298
try {
299
processEntry(entry);
300
} catch (error) {
301
console.error('Error processing resize entry:', error);
302
}
303
});
304
});
305
306
// Constructor validates arguments
307
try {
308
new ResizeObserver(); // Throws TypeError
309
} catch (error) {
310
console.error(error.message); // "1 argument required, but only 0 present."
311
}
312
313
try {
314
new ResizeObserver.call(null, () => {}); // Throws TypeError
315
} catch (error) {
316
console.error(error.message); // "Cannot call a class as a function."
317
}
318
```