svelte-intersection-observer 0.10.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,22 +12,19 @@ Try it in the [Svelte REPL](https://svelte.dev/repl/8cd2327a580c4f429c71f7df999b
12
12
 
13
13
  ## Installation
14
14
 
15
- **Yarn**
15
+ ```sh
16
+ # NPM
17
+ npm i svelte-intersection-observer
16
18
 
17
- ```bash
18
- yarn add -D svelte-intersection-observer
19
- ```
20
-
21
- **NPM**
19
+ # pnpm
20
+ pnpm i svelte-intersection-observer
22
21
 
23
- ```bash
24
- npm i -D svelte-intersection-observer
25
- ```
22
+ # Bun
23
+ bun i svelte-intersection-observer
26
24
 
27
- **pnpm**
25
+ # Yarn
26
+ yarn add svelte-intersection-observer
28
27
 
29
- ```bash
30
- pnpm i -D svelte-intersection-observer
31
28
  ```
32
29
 
33
30
  ## Usage
@@ -139,29 +136,109 @@ As an alternative to binding the `intersecting` prop, you can listen to the `int
139
136
  </IntersectionObserver>
140
137
  ```
141
138
 
139
+ ### Multiple elements
140
+
141
+ For performance, use `MultipleIntersectionObserver` to observe multiple elements.
142
+
143
+ This avoids instantiating a new observer for every element.
144
+
145
+ ```svelte
146
+ <script>
147
+ import { MultipleIntersectionObserver } from "svelte-intersection-observer";
148
+
149
+ let ref1;
150
+ let ref2;
151
+
152
+ $: elements = [ref1, ref2];
153
+ </script>
154
+
155
+ <MultipleIntersectionObserver {elements} let:elementIntersections>
156
+ <header>
157
+ <div class:intersecting={elementIntersections.get(ref1)}>
158
+ Item 1: {elementIntersections.get(ref1) ? "✓" : "✗"}
159
+ </div>
160
+ <div class:intersecting={elementIntersections.get(ref2)}>
161
+ Item 2: {elementIntersections.get(ref2) ? "✓" : "✗"}
162
+ </div>
163
+ </header>
164
+
165
+ <div bind:this={ref1}>Item 1</div>
166
+ <div bind:this={ref2}>Item 2</div>
167
+ </MultipleIntersectionObserver>
168
+ ```
169
+
142
170
  ## API
143
171
 
144
- ### Props
172
+ ### IntersectionObserver
173
+
174
+ #### Props
145
175
 
146
- | Name | Description | Type | Default value |
147
- | :----------- | :---------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------- | :------------ |
148
- | element | Observed element | `HTMLElement` | `null` |
149
- | once | Unobserve the element after the first intersection event | `boolean` | `false` |
150
- | intersecting | `true` if the observed element is intersecting the viewport | `boolean` | `false` |
151
- | root | Containing element | `null` or `HTMLElement` | `null` |
152
- | rootMargin | Margin offset of the containing element | `string` | `"0px"` |
153
- | threshold | Percentage of element visibile to trigger an event | `number` between 0 and 1, or an array of `number`s between 0 and 1 | `0` |
154
- | entry | Observed element metadata | [`IntersectionObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry) | `null` |
155
- | observer | `IntersectionObserver` instance | [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) | `null` |
176
+ | Name | Description | Type | Default value |
177
+ | :----------- | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------ | :------------ |
178
+ | element | Observed element | `null` or `HTMLElement` | `null` |
179
+ | once | Unobserve the element after the first intersection event | `boolean` | `false` |
180
+ | intersecting | `true` if the observed element is intersecting the viewport | `boolean` | `false` |
181
+ | root | Containing element | `null` or `HTMLElement` | `null` |
182
+ | rootMargin | Margin offset of the containing element | `string` | `"0px"` |
183
+ | threshold | Percentage of element visibility to trigger an event | `number` between 0 and 1, or an array of `number`s between 0 and 1 | `0` |
184
+ | entry | Observed element metadata | `null` or [`IntersectionObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry) | `null` |
185
+ | observer | `IntersectionObserver` instance | `null` or [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) | `null` |
156
186
 
157
- ### Dispatched events
187
+ #### Dispatched events
158
188
 
159
189
  - **on:observe**: fired when the element is first observed or whenever an intersection change occurs
160
190
  - **on:intersect**: fired when the element is intersecting the viewport
161
191
 
162
192
  The `e.detail` dispatched by the `observe` and `intersect` events is an [`IntersectionObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry) interface.
163
193
 
164
- Note that all properties in `IntersectionObserverEntry` are read-only.
194
+ #### Slot props
195
+
196
+ | Name | Type |
197
+ | :----------- | :------------------------------------------------------------------------------------------------------------------ |
198
+ | intersecting | `boolean` |
199
+ | entry | `null` or [`IntersectionObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry) |
200
+ | observer | [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) |
201
+
202
+ ### MultipleIntersectionObserver
203
+
204
+ #### Props
205
+
206
+ | Name | Description | Type | Default value |
207
+ | :------------------- | :---------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------- | :------------ |
208
+ | elements | Array of HTML elements to observe | `Array<HTMLElement \| null>` | `[]` |
209
+ | once | Unobserve elements after the first intersection event | `boolean` | `false` |
210
+ | root | Containing element | `null` or `HTMLElement` | `null` |
211
+ | rootMargin | Margin offset of the containing element | `string` | `"0px"` |
212
+ | threshold | Percentage of element visibility to trigger an event | `number` between 0 and 1, or an array of `number`s between 0 and 1 | `0` |
213
+ | elementIntersections | Map of each element to its intersection state | `Map<HTMLElement \| null, boolean>` | `new Map()` |
214
+ | elementEntries | Map of each element to its latest entry | `Map<HTMLElement \| null,` [`IntersectionObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry)`>` | `new Map()` |
215
+ | observer | `IntersectionObserver` instance | `null` or [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) | `null` |
216
+
217
+ #### Dispatched events
218
+
219
+ - **on:observe**: fired when an element is first observed or whenever an intersection change occurs
220
+ - **on:intersect**: fired when an element is intersecting the viewport
221
+
222
+ The `e.detail` for both events includes:
223
+
224
+ ```ts
225
+ {
226
+ entry: IntersectionObserverEntry;
227
+ target: HTMLElement;
228
+ }
229
+ ```
230
+
231
+ #### Slot props
232
+
233
+ | Name | Type |
234
+ | :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------- |
235
+ | observer | [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) |
236
+ | elementIntersections | `Map<HTMLElement \| null, boolean>` |
237
+ | elementEntries | `Map<HTMLElement \| null,` [`IntersectionObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry)`>` |
238
+
239
+ ### `IntersectionObserverEntry` interface
240
+
241
+ Note that all properties in [IntersectionObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry) are read-only.
165
242
 
166
243
  <details>
167
244
  <summary><code>IntersectionObserverEntry</code></summary>
@@ -208,21 +285,6 @@ interface IntersectionObserverEntry {
208
285
 
209
286
  </details>
210
287
 
211
- ## Examples
212
-
213
- The [examples folder](examples/) contains sample set-ups.
214
-
215
- - [examples/sveltekit](examples/sveltekit)
216
- - [examples/vite](examples/vite)
217
- - [examples/rollup](examples/rollup)
218
- - [examples/webpack](examples/webpack)
219
-
220
- ## TypeScript support
221
-
222
- Svelte version 3.31 or greater is required to use this module with TypeScript.
223
-
224
- TypeScript definitions for this component are located in the [types folder](types/).
225
-
226
288
  ## Changelog
227
289
 
228
290
  [Changelog](CHANGELOG.md)
package/package.json CHANGED
@@ -1,59 +1,39 @@
1
1
  {
2
2
  "name": "svelte-intersection-observer",
3
- "version": "0.10.2",
3
+ "version": "1.1.0",
4
4
  "license": "MIT",
5
5
  "description": "Detect if an element is in the viewport using the Intersection Observer API",
6
6
  "author": "Eric Liu (https://github.com/metonym)",
7
+ "type": "module",
7
8
  "svelte": "./src/index.js",
8
- "main": "./lib/index.js",
9
- "module": "./lib/index.mjs",
10
- "types": "./types/index.d.ts",
11
- "exports": {
12
- ".": {
13
- "types": "./types/index.d.ts",
14
- "svelte": "./src/index.js"
15
- },
16
- "./src/*.svelte": {
17
- "types": "./types/*.svelte.d.ts",
18
- "import": "./src/*.svelte"
19
- },
20
- "./src/*": {
21
- "types": "./types/*.d.ts",
22
- "import": "./src/*.js"
23
- }
24
- },
25
- "scripts": {
26
- "dev": "rollup -cw",
27
- "build": "rollup -c",
28
- "prepack": "BUNDLE=true rollup -c",
29
- "test:types": "svelte-check --workspace test",
30
- "test:examples": "node test/examples",
31
- "format": "prettier --ignore-path .gitignore --write '.'"
32
- },
33
- "devDependencies": {
34
- "prettier": "^3.0.0",
35
- "prettier-plugin-svelte": "^3.0.0",
36
- "svelte": "^4.1.0",
37
- "svelte-check": "^3.4.6",
38
- "svelte-readme": "^3.6.3"
39
- },
40
9
  "repository": {
41
10
  "type": "git",
42
- "url": "https://github.com/metonym/svelte-intersection-observer.git"
11
+ "url": "git+https://github.com/metonym/svelte-intersection-observer.git"
43
12
  },
44
13
  "homepage": "https://github.com/metonym/svelte-intersection-observer",
45
14
  "bugs": "https://github.com/metonym/svelte-intersection-observer/issues",
46
15
  "keywords": [
16
+ "svelte",
17
+ "svelte component",
47
18
  "intersection observer",
48
19
  "viewport",
49
20
  "lazy-loading",
50
- "conditional",
51
- "svelte",
52
- "svelte component"
21
+ "conditional"
53
22
  ],
54
- "files": [
55
- "lib",
56
- "src",
57
- "types"
58
- ]
59
- }
23
+ "main": "./index.js",
24
+ "types": "./index.d.ts",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./index.d.ts",
28
+ "svelte": "./index.js"
29
+ },
30
+ "./*.svelte": {
31
+ "types": "./*.svelte.d.ts",
32
+ "import": "./*.svelte"
33
+ },
34
+ "./*": {
35
+ "types": "./*.d.ts",
36
+ "import": "./*.js"
37
+ }
38
+ }
39
+ }
@@ -1,7 +1,7 @@
1
1
  <script>
2
2
  /**
3
3
  * The HTML Element to observe.
4
- * @type {HTMLElement}
4
+ * @type {null | HTMLElement}
5
5
  */
6
6
  export let element = null;
7
7
 
@@ -21,7 +21,7 @@
21
21
  /**
22
22
  * Specify the containing element.
23
23
  * Defaults to the browser viewport.
24
- * @type {HTMLElement}
24
+ * @type {null | HTMLElement}
25
25
  */
26
26
  export let root = null;
27
27
 
@@ -50,7 +50,10 @@
50
50
 
51
51
  const dispatch = createEventDispatcher();
52
52
 
53
+ /** @type {null | string} */
53
54
  let prevRootMargin = null;
55
+
56
+ /** @type {null | HTMLElement} */
54
57
  let prevElement = null;
55
58
 
56
59
  const initialize = () => {
@@ -61,7 +64,7 @@
61
64
  intersecting = _entry.isIntersecting;
62
65
  });
63
66
  },
64
- { root, rootMargin, threshold }
67
+ { root, rootMargin, threshold },
65
68
  );
66
69
  };
67
70
 
@@ -83,21 +86,21 @@
83
86
  if (entry.isIntersecting) {
84
87
  dispatch("intersect", entry);
85
88
 
86
- if (once) observer.unobserve(element);
89
+ if (element && once) observer?.unobserve(element);
87
90
  }
88
91
  }
89
92
 
90
93
  await tick();
91
94
 
92
95
  if (element !== null && element !== prevElement) {
93
- observer.observe(element);
96
+ observer?.observe(element);
94
97
 
95
- if (prevElement !== null) observer.unobserve(prevElement);
98
+ if (prevElement !== null) observer?.unobserve(prevElement);
96
99
  prevElement = element;
97
100
  }
98
101
 
99
102
  if (prevRootMargin && rootMargin !== prevRootMargin) {
100
- observer.disconnect();
103
+ observer?.disconnect();
101
104
  prevElement = null;
102
105
  initialize();
103
106
  }
@@ -0,0 +1,79 @@
1
+ import type { SvelteComponentTyped } from "svelte";
2
+
3
+ export default class extends SvelteComponentTyped<
4
+ {
5
+ /**
6
+ * The HTML Element to observe.
7
+ * @default null
8
+ */
9
+ element?: null | HTMLElement;
10
+
11
+ /**
12
+ * Set to `true` to unobserve the element
13
+ * after it intersects the viewport.
14
+ * @default false
15
+ */
16
+ once?: boolean;
17
+
18
+ /**
19
+ * `true` if the observed element
20
+ * is intersecting the viewport.
21
+ * @default false
22
+ */
23
+ intersecting?: boolean;
24
+
25
+ /**
26
+ * Specify the containing element.
27
+ * Defaults to the browser viewport.
28
+ * @default null
29
+ */
30
+ root?: null | HTMLElement;
31
+
32
+ /**
33
+ * Margin offset of the containing element.
34
+ * @default "0px"
35
+ */
36
+ rootMargin?: string;
37
+
38
+ /**
39
+ * Percentage of element visibility to trigger an event.
40
+ * Value must be a number between 0 and 1, or an array of numbers between 0 and 1.
41
+ * @default 0
42
+ */
43
+ threshold?: number | number[];
44
+
45
+ /**
46
+ * Observed element metadata.
47
+ * @default null
48
+ */
49
+ entry?: null | IntersectionObserverEntry;
50
+
51
+ /**
52
+ * `IntersectionObserver` instance.
53
+ * @default null
54
+ */
55
+ observer?: null | IntersectionObserver;
56
+ },
57
+ {
58
+ /**
59
+ * Dispatched when the element is first observed
60
+ * and also whenever an intersection event occurs.
61
+ */
62
+ observe: CustomEvent<IntersectionObserverEntry>;
63
+
64
+ /**
65
+ * Dispatched only when the element is intersecting the viewport.
66
+ * `event.detail.isIntersecting` will only be `true`
67
+ */
68
+ intersect: CustomEvent<
69
+ IntersectionObserverEntry & { isIntersecting: true }
70
+ >;
71
+ },
72
+ {
73
+ default: {
74
+ intersecting: boolean;
75
+ entry: null | IntersectionObserverEntry;
76
+ observer: IntersectionObserver;
77
+ };
78
+ }
79
+ > {}
@@ -0,0 +1,137 @@
1
+ <script>
2
+ /**
3
+ * Array of HTML Elements to observe.
4
+ * Use this for better performance when observing multiple elements.
5
+ * @type {(HTMLElement | null)[]}
6
+ */
7
+ export let elements = [];
8
+
9
+ /**
10
+ * Set to `true` to unobserve the element
11
+ * after it intersects the viewport.
12
+ * @type {boolean}
13
+ */
14
+ export let once = false;
15
+
16
+ /**
17
+ * Specify the containing element.
18
+ * Defaults to the browser viewport.
19
+ * @type {null | HTMLElement}
20
+ */
21
+ export let root = null;
22
+
23
+ /** Margin offset of the containing element. */
24
+ export let rootMargin = "0px";
25
+
26
+ /**
27
+ * Percentage of element visibility to trigger an event.
28
+ * Value must be between 0 and 1.
29
+ */
30
+ export let threshold = 0;
31
+
32
+ /**
33
+ * Map of element to its intersection state.
34
+ * @type {Map<HTMLElement | null, boolean>}
35
+ */
36
+ export let elementIntersections = new Map();
37
+
38
+ /**
39
+ * Map of element to its latest entry.
40
+ * @type {Map<HTMLElement | null, IntersectionObserverEntry>}
41
+ */
42
+ export let elementEntries = new Map();
43
+
44
+ /**
45
+ * `IntersectionObserver` instance.
46
+ * @type {null | IntersectionObserver}
47
+ */
48
+ export let observer = null;
49
+
50
+ import { tick, createEventDispatcher, afterUpdate, onMount } from "svelte";
51
+
52
+ const dispatch = createEventDispatcher();
53
+
54
+ /** @type {null | string} */
55
+ let prevRootMargin = null;
56
+
57
+ /** @type {null | HTMLElement} */
58
+ let prevElement = null;
59
+
60
+ /** @type {(HTMLElement | null)[]} */
61
+ let prevElements = [];
62
+
63
+ const initialize = () => {
64
+ observer = new IntersectionObserver(
65
+ (entries) => {
66
+ entries.forEach((_entry) => {
67
+ const target = /** @type {HTMLElement} */ (_entry.target);
68
+
69
+ elementIntersections.set(target, _entry.isIntersecting);
70
+ elementEntries.set(target, _entry);
71
+
72
+ // Trigger reactivity.
73
+ elementIntersections = new Map(elementIntersections);
74
+ elementEntries = new Map(elementEntries);
75
+
76
+ dispatch("observe", { entry: _entry, target });
77
+
78
+ if (_entry.isIntersecting) {
79
+ dispatch("intersect", { entry: _entry, target });
80
+ if (once) observer?.unobserve(target);
81
+ }
82
+ });
83
+ },
84
+ { root, rootMargin, threshold },
85
+ );
86
+ };
87
+
88
+ onMount(() => {
89
+ initialize();
90
+
91
+ return () => {
92
+ if (observer) {
93
+ observer.disconnect();
94
+ observer = null;
95
+ }
96
+ };
97
+ });
98
+
99
+ afterUpdate(async () => {
100
+ await tick();
101
+
102
+ if (elements.length > 0) {
103
+ const newElements = elements.filter(
104
+ (el) => el && !prevElements.includes(el),
105
+ );
106
+ newElements.forEach((el) => {
107
+ if (el) observer?.observe(el);
108
+ });
109
+
110
+ const removedElements = prevElements.filter(
111
+ (el) => el && !elements.includes(el),
112
+ );
113
+ removedElements.forEach((el) => {
114
+ if (el) observer?.unobserve(el);
115
+ });
116
+
117
+ prevElements = [...elements];
118
+ }
119
+
120
+ if (prevRootMargin && rootMargin !== prevRootMargin) {
121
+ observer?.disconnect();
122
+ prevElement = null;
123
+ prevElements = [];
124
+ initialize();
125
+
126
+ elements
127
+ .filter((el) => el)
128
+ .forEach((el) => {
129
+ if (el) observer?.observe(el);
130
+ });
131
+ }
132
+
133
+ prevRootMargin = rootMargin;
134
+ });
135
+ </script>
136
+
137
+ <slot {observer} {elementIntersections} {elementEntries} />
@@ -0,0 +1,82 @@
1
+ import type { SvelteComponentTyped } from "svelte";
2
+
3
+ export default class extends SvelteComponentTyped<
4
+ {
5
+ /**
6
+ * Array of HTML Elements to observe.
7
+ * Use this for better performance when observing multiple elements.
8
+ * @default []
9
+ */
10
+ elements?: (HTMLElement | null)[];
11
+
12
+ /**
13
+ * Set to `true` to unobserve the element
14
+ * after it intersects the viewport.
15
+ * @default false
16
+ */
17
+ once?: boolean;
18
+
19
+ /**
20
+ * Specify the containing element.
21
+ * Defaults to the browser viewport.
22
+ * @default null
23
+ */
24
+ root?: null | HTMLElement;
25
+
26
+ /**
27
+ * Margin offset of the containing element.
28
+ * @default "0px"
29
+ */
30
+ rootMargin?: string;
31
+
32
+ /**
33
+ * Percentage of element visibility to trigger an event.
34
+ * Value must be a number between 0 and 1, or an array of numbers between 0 and 1.
35
+ * @default 0
36
+ */
37
+ threshold?: number | number[];
38
+
39
+ /**
40
+ * Map of element to its intersection state.
41
+ * @default new Map()
42
+ */
43
+ elementIntersections?: Map<HTMLElement | null, boolean>;
44
+
45
+ /**
46
+ * Map of element to its latest entry.
47
+ * @default new Map()
48
+ */
49
+ elementEntries?: Map<HTMLElement | null, IntersectionObserverEntry>;
50
+
51
+ /**
52
+ * `IntersectionObserver` instance.
53
+ * @default null
54
+ */
55
+ observer?: null | IntersectionObserver;
56
+ },
57
+ {
58
+ /**
59
+ * Dispatched when an element is first observed
60
+ * and also whenever an intersection event occurs.
61
+ */
62
+ observe: CustomEvent<{
63
+ entry: IntersectionObserverEntry;
64
+ target: HTMLElement;
65
+ }>;
66
+
67
+ /**
68
+ * Dispatched only when an element is intersecting the viewport.
69
+ */
70
+ intersect: CustomEvent<{
71
+ entry: IntersectionObserverEntry & { isIntersecting: true };
72
+ target: HTMLElement;
73
+ }>;
74
+ },
75
+ {
76
+ default: {
77
+ observer: IntersectionObserver;
78
+ elementIntersections: Map<HTMLElement | null, boolean>;
79
+ elementEntries: Map<HTMLElement | null, IntersectionObserverEntry>;
80
+ };
81
+ }
82
+ > {}
package/src/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { default } from "./IntersectionObserver.svelte";
2
+ export { default as MultipleIntersectionObserver } from "./MultipleIntersectionObserver.svelte";
package/src/index.js CHANGED
@@ -1,3 +1,2 @@
1
- import IntersectionObserver from "./IntersectionObserver.svelte";
2
-
3
- export default IntersectionObserver;
1
+ export { default } from "./IntersectionObserver.svelte";
2
+ export { default as MultipleIntersectionObserver } from "./MultipleIntersectionObserver.svelte";