UNPKG

10.2 kBMarkdownView Raw
1<p align="center">
2 <img width="160" src="https://user-images.githubusercontent.com/1519516/68546625-51e0f680-03d0-11ea-9955-9f0e1964ba0c.png" />
3</p>
4
5<h1 align="center">Resize Observer</h1>
6
7<p align="center">
8 <img src="https://img.shields.io/circleci/project/github/juggle/resize-observer/v3.svg?logo=circleci&style=for-the-badge" />
9 <img src="https://img.shields.io/coveralls/github/juggle/resize-observer/v3.svg?logoColor=white&style=for-the-badge" />
10 <img src="https://img.shields.io/bundlephobia/minzip/@juggle/resize-observer.svg?colorB=%233399ff&style=for-the-badge" />
11 <img src="https://img.shields.io/npm/l/@juggle/resize-observer.svg?colorB=%233399ff&style=for-the-badge" />
12</p>
13
14---
15
16A minimal library which polyfills the **ResizeObserver** API and is entirely based on the latest [Draft Specification](https://drafts.csswg.org/resize-observer-1/).
17
18It immediately detects when an element resizes and provides accurate sizing information back to the handler. Check out the [Example Playground](//juggle.studio/resize-observer) for more information on usage and performance.
19
20> The latest [Resize Observer specification](https://drafts.csswg.org/resize-observer-1/) is not yet finalised and is subject to change.
21> Any drastic changes to the specification will bump the major version of this library, as there will likely be breaking changes. Check the [release notes](https://github.com/juggle/resize-observer/releases) for more information.
22
23
24## Installation
25``` shell
26npm i @juggle/resize-observer
27```
28
29## Basic usage
30``` js
31import { ResizeObserver } from '@juggle/resize-observer';
32
33const ro = new ResizeObserver((entries, observer) => {
34 console.log('Body has resized!');
35 observer.disconnect(); // Stop observing
36});
37
38ro.observe(document.body); // Watch dimension changes on body
39```
40This will use the [ponyfilled](https://github.com/sindresorhus/ponyfill) version of **ResizeObserver**, even if the browser supports **ResizeObserver** natively.
41
42## Watching multiple elements
43``` js
44import { ResizeObserver } from '@juggle/resize-observer';
45
46const ro = new ResizeObserver((entries, observer) => {
47 console.log('Elements resized:', entries.length);
48 entries.forEach((entry, index) => {
49 const { inlineSize: width, blockSize: height } = entry.contentBoxSize[0];
50 console.log(`Element ${index + 1}:`, `${width}x${height}`);
51 });
52});
53
54const els = document.querySelectorAll('.resizes');
55[...els].forEach(el => ro.observe(el)); // Watch multiple!
56```
57
58## Watching different box sizes
59
60The latest standards allow for watching different box sizes. The box size option can be specified when observing an element. Options include `border-box`, `device-pixel-content-box` and `content-box` (default).
61``` js
62import { ResizeObserver } from '@juggle/resize-observer';
63
64const ro = new ResizeObserver((entries, observer) => {
65 console.log('Elements resized:', entries.length);
66 entries.forEach((entry, index) => {
67 const [size] = entry.borderBoxSize;
68 console.log(`Element ${index + 1}:`, `${size.inlineSize}x${size.blockSize}`);
69 });
70});
71
72// Watch border-box
73const observerOptions = {
74 box: 'border-box'
75};
76
77const els = document.querySelectorAll('.resizes');
78[...els].forEach(el => ro.observe(el, observerOptions));
79```
80*From the spec:*
81> The box size properties are exposed as sequences in order to support elements that have multiple fragments, which occur in [multi-column](https://www.w3.org/TR/css3-multicol/#) scenarios. However the current definitions of content rect and border box do not mention how those boxes are affected by multi-column layout. In this spec, there will only be a single ResizeObserverSize returned in the sequences, which will correspond to the dimensions of the first column. A future version of this spec will extend the returned sequences to contain the per-fragment size information.
82
83## Using the legacy version (`contentRect`)
84
85Early versions of the API return a `contentRect`. This is still made available for backwards compatibility.
86
87``` js
88import { ResizeObserver } from '@juggle/resize-observer';
89
90const ro = new ResizeObserver((entries, observer) => {
91 console.log('Elements resized:', entries.length);
92 entries.forEach((entry, index) => {
93 const { width, height } = entry.contentRect;
94 console.log(`Element ${index + 1}:`, `${width}x${height}`);
95 });
96});
97
98const els = document.querySelectorAll('.resizes');
99[...els].forEach(el => ro.observe(el));
100```
101
102
103## Switching between native and polyfilled versions
104
105You can check to see if the native version is available and switch between this and the polyfill to improve performance on browsers with native support.
106
107``` js
108import { ResizeObserver as Polyfill } from '@juggle/resize-observer';
109
110const ResizeObserver = window.ResizeObserver || Polyfill;
111
112// Uses native or polyfill, depending on browser support.
113const ro = new ResizeObserver((entries, observer) => {
114 console.log('Something has resized!');
115});
116```
117
118To improve this even more, you could use dynamic imports to only load the file when the polyfill is required.
119
120``` js
121(async () => {
122 if ('ResizeObserver' in window === false) {
123 // Loads polyfill asynchronously, only if required.
124 const module = await import('@juggle/resize-observer');
125 window.ResizeObserver = module.ResizeObserver;
126 }
127 // Uses native or polyfill, depending on browser support.
128 const ro = new ResizeObserver((entries, observer) => {
129 console.log('Something has resized!');
130 });
131})();
132```
133
134> Browsers with native support may be behind on the latest specification.
135> Use `entry.contentRect` when switching between native and polyfilled versions.
136
137
138## Resize loop detection
139
140Resize Observers have inbuilt protection against infinite resize loops.
141
142If an element's observed box size changes again within the same resize loop, the observation will be skipped and an error event will be dispatched on the window. Elements with undelivered notifications will be considered for delivery in the next loop.
143
144```js
145import { ResizeObserver } from '@juggle/resize-observer';
146
147const ro = new ResizeObserver((entries, observer) => {
148 // Changing the body size inside of the observer
149 // will cause a resize loop and the next observation will be skipped
150 document.body.style.width = '50%';
151});
152
153// Listen for errors
154window.addEventListener('error', e => console.log(e.message));
155
156// Observe the body
157ro.observe(document.body);
158```
159
160## Notification Schedule
161Notifications are scheduled after all other changes have occurred and all other animation callbacks have been called. This allows the observer callback to get the most accurate size of an element, as no other changes should occur in the same frame.
162
163![resize observer notification schedule](https://user-images.githubusercontent.com/1519516/52825568-20433500-30b5-11e9-9854-4cee13a09a7d.jpg)
164
165
166
167## How are differences detected?
168
169To prevent constant polling, every frame. The DOM is queried whenever an event occurs which could cause an element to change its size. This could be when an element is clicked, a DOM Node is added, or, when an animation is running.
170
171To cover these scenarios, there are two types of observation. The first is to listen to specific DOM events, including `resize`, `mousedown` and `focus` to name a few. The second is to listen for any DOM mutations that occur. This detects when a DOM node is added or removed, an attribute is modified, or, even when some text has changed.
172
173This allows for greater idle time, when the application itself is idle.
174
175
176## Features
177
178- Inbuilt resize loop protection.
179- Supports pseudo classes `:hover`, `:active` and `:focus`.
180- Supports transitions and animations, including infinite and long-running.
181- Detects changes which occur during animation frame.
182- Includes support for latest draft spec - observing different box sizes.
183- Polls only when required, then shuts down automatically, reducing CPU usage.
184- Zero delay system - Notifications are batched and delivered immediately, before the next paint.
185
186
187## Limitations
188
189- Transitions with initial delays cannot be detected.*
190- Animations and transitions with long periods of no change, will not be detected.*
191- Style changes from dev tools will only be noticed if they are inline styles.*
192
193
194## Tested Browsers
195
196[chrome]: https://github.com/alrra/browser-logos/raw/master/src/chrome/chrome_64x64.png
197[safari]: https://github.com/alrra/browser-logos/raw/master/src/safari/safari_64x64.png
198[safari-ios]: https://github.com/alrra/browser-logos/raw/master/src/safari-ios/safari-ios_64x64.png
199[ff]: https://github.com/alrra/browser-logos/raw/master/src/firefox/firefox_64x64.png
200[opera]: https://github.com/alrra/browser-logos/raw/master/src/opera/opera_64x64.png
201[opera-mini]: https://github.com/alrra/browser-logos/raw/master/src/opera-mini/opera-mini_64x64.png
202[edge_12-18]: https://github.com/alrra/browser-logos/raw/master/src/archive/edge_12-18/edge_12-18_64x64.png
203[edge]: https://github.com/alrra/browser-logos/raw/master/src/edge/edge_64x64.png
204[samsung]: https://github.com/alrra/browser-logos/raw/master/src/samsung-internet/samsung-internet_64x64.png
205[ie]: https://github.com/alrra/browser-logos/raw/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_64x64.png
206
207### Desktop
208| ![chrome][chrome] | ![safari][safari] | ![ff][ff] | ![opera][opera] | ![edge][edge] | ![edge][edge_12-18] | ![IE][ie] |
209|--------|--------|---------|-------|------|------------|---------------------------------------|
210| Chrome | Safari | Firefox | Opera | Edge | Edge 12-18 | IE11<br/>IE 9-10 (with polyfills)\*\* |
211
212### Mobile
213| ![chrome][chrome] | ![safari][safari] | ![ff][ff] | ![opera][opera] | ![opera mini][opera-mini] | ![edge][edge_12-18] | ![samsung internet][samsung] |
214|--------|--------|---------|-------|------------|------|------------------|
215| Chrome | Safari | Firefox | Opera | Opera Mini | Edge | Samsung Internet |
216
217---
218
219\*If other interaction occurs, changes will be detected.
220
221\*\*IE10 requires additional polyfills for `WeakMap`, `MutationObserver` and `devicePixelRatio`. IE9 requires IE10 polyfills plus `requestAnimationFrame`. For more information, [see issue here](https://github.com/juggle/resize-observer/issues/64).