Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add MultipleIntersectionObserver #95

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

metonym
Copy link
Owner

@metonym metonym commented Mar 2, 2025

A common scenario is to have multiple elements for observing. Currently, the library only allows one element to be observed at a time. For large number of items, this is less performant than using a single observer for multiple elements.

This introduces MultipleIntersectionObserver, which allows multiple elements to be observed by a single instance. The ergonomics of using it is a bit different, in that an array is provided and a Map returned.

The most basic, proposed usage is as follows:

<script>
  import { MultipleIntersectionObserver } from "svelte-intersection-observer";

  let ref1;
  let ref2;

  $: elements = [ref1, ref2];
</script>

<header />

<MultipleIntersectionObserver {elements} let:elementIntersections>
  {#each elements as element, index}
    {@const visible = elementIntersections.get(element)}

    <div bind:this={element} class:visible>
      Item {index + 1}
    </div>
  {/each}
</MultipleIntersectionObserver>

Another example is binding to the map directly to re-use state elsewhere in the component:

<script>
  import { MultipleIntersectionObserver } from "svelte-intersection-observer";

  let ref1;
  let ref2;
  let elementIntersections = new Map();

  $: elements = [ref1, ref2];
  $: anyItemVisible = Array.from(elementIntersections).some(
    (visible) => visible,
  );
</script>

<header class:intersecting={anyItemVisible}>
  {#each elements as element, index}
    {@const visible = elementIntersections.get(element)}
    {#if visible}
      Item {index + 1} visible
    {/if}
  {/each}
</header>

<MultipleIntersectionObserver
  {elements}
  bind:elementIntersections
  let:elementIntersections
>
  {#each elements as element, index}
    {@const visible = elementIntersections.get(element)}

    <div bind:this={element} class:visible>
      Item {index + 1} ({visible})
    </div>
  {/each}
</MultipleIntersectionObserver>

However, it's simpler to wrap the sibling element with the MultipleIntersectionObserver instance, and use the destructed let:elementIntersections directly instead of binding to it.

<script>
  import { MultipleIntersectionObserver } from "svelte-intersection-observer";

  let ref1;
  let ref2;

  $: elements = [ref1, ref2];
</script>

<MultipleIntersectionObserver {elements} let:elementIntersections>
  {@const anyItemVisible = Array.from(elementIntersections).some(
    (visible) => visible,
  )}
  <header class:intersecting={anyItemVisible}>
    {#each elements as element, index}
      {@const visible = elementIntersections.get(element)}
      {#if visible}
        Item {index + 1} visible
      {/if}
    {/each}
  </header>

  {#each elements as element, index}
    {@const visible = elementIntersections.get(element)}

    <div bind:this={element} class:visible>
      Item {index + 1} ({visible})
    </div>
  {/each}
</MultipleIntersectionObserver>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant