Lets assume I have Icon.svelte component in /elements
folder. I have used that Icon component in various other components in whole application.
Is there any way to set intersection observer for that Icon component? So when that component comes in Viewport it mount and on outside of Viewport it destroys!
Basically thinking this approach for performance boost up of application.
First of all, you should measure whether any action is even necessary.
If so, you could try simply creating an observer in each component, and only if that is too expensive extract the observer instance.
To do that you can create the observer at the top level of your application and set a context that passes the observer on to all descendants. Then in the icon component you can get the context and call observe
in onMount
and unobserve
in onDestroy
. You would want to observe some container element in the component.
You may have to add something like an EventTarget
to the context so the icons have something to get events from. In the observer callback a new event can then be dispatched to that to notify the components. The arguments from the callbacks have to be passed on, so the icon can check whether it was among the intersected elements. (Event subscription and unsubscription should be done in onMount
/ onDestroy
.)
Example:
// In root
const context = setContext('intersection-observer', writable(null));
onMount(() => {
const event = new EventTarget();
const observer = new IntersectionObserver(
entries => event.dispatchEvent(new CustomEvent('intersect', { detail: entries })),
{ root: null, rootMargin: '0px', threshold: 0 },
);
$context = {
observe: element => observer.observe(element),
unobserve: element => observer.unobserve(element),
onIntersect: event,
};
return () => observer.disconnect();
});
<!-- Component that uses the observer -->
<script>
import { getContext } from 'svelte';
import { onDestroy } from 'svelte';
const intersectionContext = getContext('intersection-observer');
const cleanup = [];
let root;
let visible = false;
onDestroy(() => cleanup.forEach(fn => fn()));
$: if ($intersectionContext && root) {
$intersectionContext.observe(root);
cleanup.push(() => $intersectionContext.unobserve(root));
$intersectionContext.onIntersect.addEventListener('intersect', onIntersect);
cleanup.push(() =>
$intersectionContext.onIntersect
.removeEventListener('intersect', onIntersect)
);
}
function onIntersect(e) {
const entries = e.detail;
const entry = entries.find(entry => entry.target === root);
if (entry)
visible = entry.isIntersecting;
}
</script>
<div bind:this={root}>
<!-- Render expensive content here using {#if visible} -->
{visible ? 'Visible' : 'Invisible'}
</div>
(This is a bit more complicated than what I described because this is designed to support SSR. To support SSR, APIs like IntersectionObserver
cannot be used outside of onMount
.)
If you do not mind a bit more convention-based magic, you can just tag elements in some way, eg using a data
attribute and then send events straight to those elements.
// In root
onMount(() => {
const intersectionObserver = new IntersectionObserver(
entries => entries.forEach(entry =>
entry.target.dispatchEvent(
new CustomEvent('intersect', { detail: entry })
)
),
{ root: null, rootMargin: '0px', threshold: 0 },
);
const mutationObserver = new MutationObserver(mutations =>
mutations.forEach(m => {
m.addedNodes.forEach(node => {
if (node instanceof HTMLElement &&
node.dataset.intersect != null &&
node.dataset.intersectInitialized == null) {
intersectionObserver.observe(node);
node.dataset.intersectInitialized = 'true';
}
});
m.removedNodes.forEach(node => {
if (node instanceof HTMLElement) {
intersectionObserver.unobserve(node);
}
});
})
);
[...document.querySelectorAll('[data-intersect]')].forEach(node => {
intersectionObserver.observe(node);
node.dataset.intersectInitialized = 'true';
});
mutationObserver.observe(document.body, { childList: true, subtree: true });
return () => {
mutationObserver.disconnect();
intersectionObserver.disconnect();
};
});
<script>
let visible = false;
</script>
<div data-intersect
on:intersect={e => visible = e.detail.isIntersecting}>
<!-- Render expensive content here using {#if visible} -->
{visible ? 'Visible' : 'Invisible'}
</div>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.