svelte-intersection-observer 1.0.0 → 1.1.1

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.
@@ -1,6 +1,4 @@
1
1
  <script>
2
- // @ts-check
3
-
4
2
  /**
5
3
  * The HTML Element to observe.
6
4
  * @type {null | HTMLElement}
@@ -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/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
145
173
 
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` |
174
+ #### Props
156
175
 
157
- ### Dispatched events
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` |
186
+
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>
package/index.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { default } from "./IntersectionObserver.svelte";
2
+ export { default as MultipleIntersectionObserver } from "./MultipleIntersectionObserver.svelte";
package/index.js CHANGED
@@ -1 +1,2 @@
1
1
  export { default } from "./IntersectionObserver.svelte";
2
+ export { default as MultipleIntersectionObserver } from "./MultipleIntersectionObserver.svelte";
package/package.json CHANGED
@@ -1,11 +1,25 @@
1
1
  {
2
2
  "name": "svelte-intersection-observer",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
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
7
  "type": "module",
8
- "svelte": "./index.js",
8
+ "svelte": "./src/index.js",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/metonym/svelte-intersection-observer.git"
12
+ },
13
+ "homepage": "https://github.com/metonym/svelte-intersection-observer",
14
+ "bugs": "https://github.com/metonym/svelte-intersection-observer/issues",
15
+ "keywords": [
16
+ "svelte",
17
+ "svelte component",
18
+ "intersection observer",
19
+ "viewport",
20
+ "lazy-loading",
21
+ "conditional"
22
+ ],
9
23
  "main": "./index.js",
10
24
  "types": "./index.d.ts",
11
25
  "exports": {
@@ -21,19 +35,5 @@
21
35
  "types": "./*.d.ts",
22
36
  "import": "./*.js"
23
37
  }
24
- },
25
- "repository": {
26
- "type": "git",
27
- "url": "https://github.com/metonym/svelte-intersection-observer.git"
28
- },
29
- "homepage": "https://github.com/metonym/svelte-intersection-observer",
30
- "bugs": "https://github.com/metonym/svelte-intersection-observer/issues",
31
- "keywords": [
32
- "svelte",
33
- "svelte component",
34
- "intersection observer",
35
- "viewport",
36
- "lazy-loading",
37
- "conditional"
38
- ]
38
+ }
39
39
  }