svelte-intersection-observer 2.0.0 → 2.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.
@@ -48,7 +48,11 @@
48
48
  onobserve?.(_entry);
49
49
 
50
50
  if (_entry.isIntersecting) {
51
- onintersect?.(_entry);
51
+ onintersect?.(
52
+ /** @type {IntersectionObserverEntry & { isIntersecting: true }} */ (
53
+ _entry
54
+ ),
55
+ );
52
56
 
53
57
  if (element && once) observer?.unobserve(element);
54
58
  }
@@ -50,7 +50,13 @@
50
50
  onobserve?.({ entry: _entry, target });
51
51
 
52
52
  if (_entry.isIntersecting) {
53
- onintersect?.({ entry: _entry, target });
53
+ onintersect?.({
54
+ entry:
55
+ /** @type {IntersectionObserverEntry & { isIntersecting: true }} */ (
56
+ _entry
57
+ ),
58
+ target,
59
+ });
54
60
  if (once) observer?.unobserve(target);
55
61
  }
56
62
  }
package/README.md CHANGED
@@ -1,12 +1,19 @@
1
1
  # svelte-intersection-observer
2
2
 
3
+ <!-- HIDE_START -->
3
4
  [![NPM][npm]][npm-url]
5
+ <!-- HIDE_END -->
4
6
 
5
7
  > Detect if an element is in the viewport using the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
6
8
 
7
9
  Use it to lazy-load images, trigger scroll animations, implement infinite scroll, autoplay video when visible, track ad or analytics impressions, or detect when a user has scrolled to the end of a list.
8
10
 
9
- This library is zero-dependency and offers three ways to observe elements: the `IntersectionObserver` component for a single element, the `MultipleIntersectionObserver` component for observing many elements with one shared observer (better performance than one observer per element), and the `intersect` action for observing an element directly with `use:`.
11
+ This zero-dependency library offers several ways to observe elements:
12
+
13
+ - `IntersectionObserver`: component for observing a single element
14
+ - `MultipleIntersectionObserver`: component for observing multiple elements (shared observer for better performance)
15
+ - `intersect`: action for observing an element directly with `use:`
16
+ - `intersectAttachment`: attachment for observing an element directly with `{@attach}` (Svelte 5.29+)
10
17
 
11
18
  Try it in the [Svelte REPL](https://svelte.dev/repl/8cd2327a580c4f429c71f7df999bd51d).
12
19
 
@@ -263,6 +270,37 @@ As an alternative to the `IntersectionObserver` component, use the `intersect` a
263
270
 
264
271
  Options passed to `use:intersect` are reactive: updating `root`, `rootMargin`, or `threshold` re-initializes the underlying observer. Updating `skip` toggles observing on the existing observer without re-initializing it.
265
272
 
273
+ ### `intersectAttachment` attachment
274
+
275
+ As of Svelte 5.29, [attachments](https://svelte.dev/docs/svelte/svelte-attachments) are the preferred replacement for actions. `intersectAttachment` wraps the `intersect` action with `svelte/attachments`'s `fromAction`, reusing the same observer logic but plugging into `{@attach ...}` instead of `use:`.
276
+
277
+ Attachments have a few architectural advantages over actions:
278
+
279
+ - No separate `update()` lifecycle method; they rerun reactively like a `$effect`
280
+ - Just plain functions, so they're easier to compose and generate dynamically
281
+ - Can be forwarded through components as ordinary props, unlike actions
282
+
283
+ ```svelte
284
+ <script>
285
+ import { intersectAttachment } from "svelte-intersection-observer";
286
+
287
+ let attachmentIntersecting = $state(false);
288
+ </script>
289
+
290
+ <header class:intersecting={attachmentIntersecting}>
291
+ {attachmentIntersecting ? "Element is in view" : "Element is not in view"}
292
+ </header>
293
+
294
+ <div
295
+ {@attach intersectAttachment(() => ({ once: true }))}
296
+ onobserve={(e) => (attachmentIntersecting = e.detail.isIntersecting)}
297
+ >
298
+ Hello world
299
+ </div>
300
+ ```
301
+
302
+ **Note**: unlike `use:intersect`, which takes the options object directly, `intersectAttachment` takes a function that _returns_ the options object (this is how `fromAction` tracks reactive dependencies). `intersect` remains fully supported; use whichever fits your codebase.
303
+
266
304
  ### Multiple elements
267
305
 
268
306
  For performance, use `MultipleIntersectionObserver` to observe multiple elements with a single shared observer instead of instantiating a new one for every element.
@@ -417,7 +455,9 @@ Both callbacks are called with:
417
455
  | elementIntersections | `Map<HTMLElement \| null, boolean>` |
418
456
  | elementEntries | `Map<HTMLElement \| null,` [`IntersectionObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry)`>` |
419
457
 
420
- ### `intersect` action
458
+ ### `intersect` action / `intersectAttachment` attachment
459
+
460
+ `intersectAttachment` is a thin wrapper around the `intersect` action (see [above](#intersectattachment-attachment)), so both share the same options and dispatched events.
421
461
 
422
462
  #### Options
423
463
 
package/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { default } from "./IntersectionObserver.svelte";
2
2
  export type { IntersectActionOptions } from "./intersect.svelte.js";
3
- export { intersect } from "./intersect.svelte.js";
3
+ export { intersect, intersectAttachment } from "./intersect.svelte.js";
4
4
  export { default as MultipleIntersectionObserver } from "./MultipleIntersectionObserver.svelte";
package/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  export { default } from "./IntersectionObserver.svelte";
2
- export { intersect } from "./intersect.svelte.js";
2
+ export { intersect, intersectAttachment } from "./intersect.svelte.js";
3
3
  export { default as MultipleIntersectionObserver } from "./MultipleIntersectionObserver.svelte";
@@ -1,4 +1,5 @@
1
1
  import type { Action } from "svelte/action";
2
+ import type { Attachment } from "svelte/attachments";
2
3
 
3
4
  export interface IntersectActionOptions {
4
5
  /**
@@ -51,3 +52,20 @@ export const intersect: Action<
51
52
  ) => void;
52
53
  }
53
54
  >;
55
+
56
+ /**
57
+ * Svelte attachment that observes the element with the Intersection Observer
58
+ * API. Equivalent to `intersect`, for use with `{@attach}` instead of `use:`.
59
+ */
60
+ export function intersectAttachment(
61
+ getOptions?: () => IntersectActionOptions,
62
+ ): Attachment<HTMLElement>;
63
+
64
+ declare module "svelte/elements" {
65
+ export interface HTMLAttributes<T> {
66
+ onobserve?: (event: CustomEvent<IntersectionObserverEntry>) => void;
67
+ onintersect?: (
68
+ event: CustomEvent<IntersectionObserverEntry & { isIntersecting: true }>,
69
+ ) => void;
70
+ }
71
+ }
@@ -1,3 +1,5 @@
1
+ import { fromAction } from "svelte/attachments";
2
+
1
3
  /**
2
4
  * Svelte action that observes `node` with the Intersection Observer API.
3
5
  * Dispatches `observe` (on every change) and `intersect` (on entering the
@@ -67,3 +69,13 @@ export function intersect(node, options = {}) {
67
69
  },
68
70
  };
69
71
  }
72
+
73
+ /**
74
+ * Svelte attachment that observes the element with the Intersection Observer
75
+ * API. Equivalent to the `intersect` action, for use with `{@attach}` instead
76
+ * of `use:`. Wraps `intersect` via `svelte/attachments`'s `fromAction`.
77
+ * @param {() => import("./intersect.svelte.d.ts").IntersectActionOptions} [getOptions]
78
+ */
79
+ export function intersectAttachment(getOptions = () => ({})) {
80
+ return fromAction(intersect, getOptions);
81
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-intersection-observer",
3
- "version": "2.0.0",
3
+ "version": "2.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)",