fix: unobserve entry.target for once, not the current element prop#153
Merged
Conversation
Intersection callbacks are delivered asynchronously. If the `element` prop changed between observation and delivery, a stale entry for the old element ran `unobserve(element)` against the *new* element, leaving the new element unobserved and the wiring silently dead. Unobserve the entry's own target instead. MultipleIntersectionObserver and createIntersectionGroup already used entry.target; only the single-element component was affected.
render
Bot
temporarily deployed
to
metonym/fix-once-unobserve-target - svelte-intersection-observer PR #153
July 5, 2026 20:21
Destroyed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The once callback in IntersectionObserver.svelte was unobserving the current element prop rather than the entry's own target. Since IntersectionObserver callbacks are delivered asynchronously, a stale entry could arrive after the element prop had already changed, causing unobserve to run against the new element instead of the one the entry actually belonged to. That left the new element unobserved and the old one potentially still observed, silently breaking the wiring after a fast element swap with once set.
The fix unobserves entry.target directly, matching the approach already used in MultipleIntersectionObserver and createIntersectionGroup.
Added a fixture and unit test that reproduce the race by swapping the observed element and then delivering a stale entry for the old element, confirming the new element stays observed and the old one does not. The test fails against the prior implementation and passes with the fix. Risk is low since the change only affects the once code path and mirrors existing, already-correct behavior elsewhere in the codebase.