svelte-intersection-observer 2.1.0 → 2.2.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/IntersectionObserver.svelte +23 -23
- package/MultipleIntersectionObserver.svelte +41 -40
- package/README.md +424 -269
- package/create-intersection-observer.svelte.d.ts +22 -0
- package/create-intersection-observer.svelte.js +36 -0
- package/index.d.ts +13 -2
- package/index.js +6 -1
- package/intersect.svelte.d.ts +72 -0
- package/intersect.svelte.js +70 -24
- package/package.json +2 -2
|
@@ -1,23 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { untrack } from "svelte";
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* @typedef {Object} Props
|
|
6
|
-
* @property {null | HTMLElement} [element] The HTML Element to observe.
|
|
7
|
-
* @property {boolean} [once] Set to `true` to unobserve the element after it intersects the viewport.
|
|
8
|
-
* @property {boolean} [intersecting] `true` if the observed element is intersecting the viewport.
|
|
9
|
-
* @property {null | HTMLElement} [root] Specify the containing element. Defaults to the browser viewport.
|
|
10
|
-
* @property {string} [rootMargin] Margin offset of the containing element.
|
|
11
|
-
* @property {number | number[]} [threshold] Percentage of element visibility to trigger an event. Value must be between 0 and 1.
|
|
12
|
-
* @property {null | IntersectionObserverEntry} [entry] Observed element metadata.
|
|
13
|
-
* @property {null | IntersectionObserver} [observer] `IntersectionObserver` instance.
|
|
14
|
-
* @property {boolean} [skip] Set to `true` to pause observing without disconnecting the observer or losing `entry`/`intersecting` state. Set back to `false` to resume.
|
|
15
|
-
* @property {(entry: IntersectionObserverEntry) => void} [onobserve] Called when the element is first observed and also whenever an intersection event occurs.
|
|
16
|
-
* @property {(entry: IntersectionObserverEntry & { isIntersecting: true }) => void} [onintersect] Called only when the observed element is intersecting the viewport.
|
|
17
|
-
* @property {import("svelte").Snippet<[{ intersecting: boolean, entry: null | IntersectionObserverEntry, observer: null | IntersectionObserver }]>} [children]
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
/** @type {Props} */
|
|
21
4
|
let {
|
|
22
5
|
element = null,
|
|
23
6
|
once = false,
|
|
@@ -33,12 +16,20 @@
|
|
|
33
16
|
children,
|
|
34
17
|
} = $props();
|
|
35
18
|
|
|
36
|
-
/** @type {null | HTMLElement} */
|
|
37
19
|
let prevElement = null;
|
|
38
20
|
|
|
39
21
|
let prevSkip = untrack(() => skip);
|
|
40
22
|
|
|
23
|
+
const configKey = $derived(
|
|
24
|
+
`${root}|${rootMargin}|${JSON.stringify(threshold)}`,
|
|
25
|
+
);
|
|
26
|
+
|
|
41
27
|
const initialize = () => {
|
|
28
|
+
if (typeof IntersectionObserver === "undefined") {
|
|
29
|
+
observer = null;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
42
33
|
observer = new IntersectionObserver(
|
|
43
34
|
(entries) => {
|
|
44
35
|
for (const _entry of entries) {
|
|
@@ -49,7 +40,7 @@
|
|
|
49
40
|
|
|
50
41
|
if (_entry.isIntersecting) {
|
|
51
42
|
onintersect?.(
|
|
52
|
-
|
|
43
|
+
(
|
|
53
44
|
_entry
|
|
54
45
|
),
|
|
55
46
|
);
|
|
@@ -63,8 +54,12 @@
|
|
|
63
54
|
};
|
|
64
55
|
|
|
65
56
|
$effect(() => {
|
|
66
|
-
|
|
67
|
-
|
|
57
|
+
configKey;
|
|
58
|
+
|
|
59
|
+
untrack(() => {
|
|
60
|
+
prevElement = null;
|
|
61
|
+
initialize();
|
|
62
|
+
});
|
|
68
63
|
|
|
69
64
|
return () => {
|
|
70
65
|
observer?.disconnect();
|
|
@@ -77,10 +72,15 @@
|
|
|
77
72
|
const isSkipped = skip;
|
|
78
73
|
const activeObserver = observer;
|
|
79
74
|
|
|
80
|
-
if (target !==
|
|
81
|
-
if (!isSkipped) activeObserver?.observe(target);
|
|
75
|
+
if (target !== prevElement) {
|
|
82
76
|
if (prevElement !== null) activeObserver?.unobserve(prevElement);
|
|
77
|
+
if (target !== null && !isSkipped) activeObserver?.observe(target);
|
|
83
78
|
prevElement = target;
|
|
79
|
+
|
|
80
|
+
if (target === null) {
|
|
81
|
+
intersecting = false;
|
|
82
|
+
entry = null;
|
|
83
|
+
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
if (isSkipped !== prevSkip && target !== null) {
|
|
@@ -1,23 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { untrack } from "svelte";
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* @typedef {Object} Props
|
|
6
|
-
* @property {(HTMLElement | null)[]} [elements] Array of HTML Elements to observe. Use this for better performance when observing multiple elements.
|
|
7
|
-
* @property {boolean} [once] Set to `true` to unobserve the element after it intersects the viewport.
|
|
8
|
-
* @property {null | HTMLElement} [root] Specify the containing element. Defaults to the browser viewport.
|
|
9
|
-
* @property {string} [rootMargin] Margin offset of the containing element.
|
|
10
|
-
* @property {number | number[]} [threshold] Percentage of element visibility to trigger an event. Value must be between 0 and 1.
|
|
11
|
-
* @property {Map<HTMLElement | null, boolean>} [elementIntersections] Map of element to its intersection state.
|
|
12
|
-
* @property {Map<HTMLElement | null, IntersectionObserverEntry>} [elementEntries] Map of element to its latest entry.
|
|
13
|
-
* @property {null | IntersectionObserver} [observer] `IntersectionObserver` instance.
|
|
14
|
-
* @property {boolean} [skip] Set to `true` to pause observing all elements without disconnecting the observer or losing `elementIntersections`/`elementEntries` state. Set back to `false` to resume.
|
|
15
|
-
* @property {(detail: { entry: IntersectionObserverEntry, target: HTMLElement }) => void} [onobserve] Called when an element is first observed and also whenever an intersection event occurs.
|
|
16
|
-
* @property {(detail: { entry: IntersectionObserverEntry & { isIntersecting: true }, target: HTMLElement }) => void} [onintersect] Called only when an element is intersecting the viewport.
|
|
17
|
-
* @property {import("svelte").Snippet<[{ observer: null | IntersectionObserver, elementIntersections: Map<HTMLElement | null, boolean>, elementEntries: Map<HTMLElement | null, IntersectionObserverEntry> }]>} [children]
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
/** @type {Props} */
|
|
21
4
|
let {
|
|
22
5
|
elements = [],
|
|
23
6
|
once = false,
|
|
@@ -33,16 +16,24 @@
|
|
|
33
16
|
children,
|
|
34
17
|
} = $props();
|
|
35
18
|
|
|
36
|
-
|
|
37
|
-
let prevElements = [];
|
|
19
|
+
let prevElements = new Set();
|
|
38
20
|
|
|
39
21
|
let prevSkip = untrack(() => skip);
|
|
40
22
|
|
|
23
|
+
const configKey = $derived(
|
|
24
|
+
`${root}|${rootMargin}|${JSON.stringify(threshold)}`,
|
|
25
|
+
);
|
|
26
|
+
|
|
41
27
|
const initialize = () => {
|
|
28
|
+
if (typeof IntersectionObserver === "undefined") {
|
|
29
|
+
observer = null;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
42
33
|
observer = new IntersectionObserver(
|
|
43
34
|
(entries) => {
|
|
44
35
|
for (const _entry of entries) {
|
|
45
|
-
const target =
|
|
36
|
+
const target = (_entry.target);
|
|
46
37
|
|
|
47
38
|
elementIntersections.set(target, _entry.isIntersecting);
|
|
48
39
|
elementEntries.set(target, _entry);
|
|
@@ -52,7 +43,7 @@
|
|
|
52
43
|
if (_entry.isIntersecting) {
|
|
53
44
|
onintersect?.({
|
|
54
45
|
entry:
|
|
55
|
-
|
|
46
|
+
(
|
|
56
47
|
_entry
|
|
57
48
|
),
|
|
58
49
|
target,
|
|
@@ -61,7 +52,6 @@
|
|
|
61
52
|
}
|
|
62
53
|
}
|
|
63
54
|
|
|
64
|
-
// Trigger reactivity once per batch, not once per entry.
|
|
65
55
|
elementIntersections = new Map(elementIntersections);
|
|
66
56
|
elementEntries = new Map(elementEntries);
|
|
67
57
|
},
|
|
@@ -70,8 +60,12 @@
|
|
|
70
60
|
};
|
|
71
61
|
|
|
72
62
|
$effect(() => {
|
|
73
|
-
|
|
74
|
-
|
|
63
|
+
configKey;
|
|
64
|
+
|
|
65
|
+
untrack(() => {
|
|
66
|
+
prevElements = new Set();
|
|
67
|
+
initialize();
|
|
68
|
+
});
|
|
75
69
|
|
|
76
70
|
return () => {
|
|
77
71
|
observer?.disconnect();
|
|
@@ -84,28 +78,35 @@
|
|
|
84
78
|
const isSkipped = skip;
|
|
85
79
|
const activeObserver = observer;
|
|
86
80
|
|
|
87
|
-
|
|
88
|
-
const newElements = currentElements.filter(
|
|
89
|
-
(el) => el && !prevElements.includes(el),
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
if (!isSkipped) {
|
|
93
|
-
for (const el of newElements) {
|
|
94
|
-
if (el) activeObserver?.observe(el);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
81
|
+
const currentSet = new Set(currentElements);
|
|
97
82
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
83
|
+
const newElements = currentElements.filter(
|
|
84
|
+
(el) => el && !prevElements.has(el),
|
|
85
|
+
);
|
|
86
|
+
const removedElements = [...prevElements].filter(
|
|
87
|
+
(el) => el && !currentSet.has(el),
|
|
88
|
+
);
|
|
101
89
|
|
|
102
|
-
|
|
103
|
-
|
|
90
|
+
if (!isSkipped) {
|
|
91
|
+
for (const el of newElements) {
|
|
92
|
+
if (el) activeObserver?.observe(el);
|
|
104
93
|
}
|
|
94
|
+
}
|
|
105
95
|
|
|
106
|
-
|
|
96
|
+
for (const el of removedElements) {
|
|
97
|
+
if (!el) continue;
|
|
98
|
+
activeObserver?.unobserve(el);
|
|
99
|
+
elementIntersections.delete(el);
|
|
100
|
+
elementEntries.delete(el);
|
|
107
101
|
}
|
|
108
102
|
|
|
103
|
+
if (removedElements.length > 0) {
|
|
104
|
+
elementIntersections = new Map(elementIntersections);
|
|
105
|
+
elementEntries = new Map(elementEntries);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
prevElements = currentSet;
|
|
109
|
+
|
|
109
110
|
if (isSkipped !== prevSkip) {
|
|
110
111
|
for (const el of currentElements) {
|
|
111
112
|
if (!el) continue;
|