diff --git a/src/IntersectionObserver.svelte b/src/IntersectionObserver.svelte index b86199f..19e6bd1 100644 --- a/src/IntersectionObserver.svelte +++ b/src/IntersectionObserver.svelte @@ -63,7 +63,7 @@ ), ); - if (element && once) observer?.unobserve(element); + if (once) observer?.unobserve(_entry.target); } } }, diff --git a/tests/e2e/fixtures/OnceElementChangeFixture.svelte b/tests/e2e/fixtures/OnceElementChangeFixture.svelte new file mode 100644 index 0000000..0693928 --- /dev/null +++ b/tests/e2e/fixtures/OnceElementChangeFixture.svelte @@ -0,0 +1,63 @@ + + +
+ {intersecting ? "Element is in view" : "Element is not in view"} + +
+ + +
+ A +
+
+ +
+ B +
+ + diff --git a/tests/unit/IntersectionObserver.test.ts b/tests/unit/IntersectionObserver.test.ts index 645c2f0..edf7bb8 100644 --- a/tests/unit/IntersectionObserver.test.ts +++ b/tests/unit/IntersectionObserver.test.ts @@ -4,6 +4,7 @@ import BasicFixture from "../e2e/fixtures/BasicFixture.svelte"; import EachBindingFixture from "../e2e/fixtures/EachBindingFixture.svelte"; import ElementChangeFixture from "../e2e/fixtures/ElementChangeFixture.svelte"; import ElementNullFixture from "../e2e/fixtures/ElementNullFixture.svelte"; +import OnceElementChangeFixture from "../e2e/fixtures/OnceElementChangeFixture.svelte"; import OnceFixture from "../e2e/fixtures/OnceFixture.svelte"; import RootFixture from "../e2e/fixtures/RootFixture.svelte"; import RootMarginChangeFixture from "../e2e/fixtures/RootMarginChangeFixture.svelte"; @@ -80,6 +81,36 @@ describe("IntersectionObserver", () => { ).toContain("Intersect count: 1"); }); + test("once unobserves the entry's own target, not a stale element prop, when the element changes before delivery", () => { + const rendered = render(OnceElementChangeFixture); + cleanup = rendered.cleanup; + const elementA = rendered.target.querySelector('[data-testid="el-a"]'); + const elementB = rendered.target.querySelector('[data-testid="el-b"]'); + const switchButton = rendered.target.querySelector( + '[data-testid="switch"]', + ); + if ( + !elementA || + !elementB || + !(switchButton instanceof HTMLButtonElement) + ) { + throw new Error("fixture markup missing"); + } + + const observer = MockIntersectionObserver.last(); + expect(observer.observedElements.has(elementA)).toBe(true); + + switchButton.click(); + flushSync(); + + flushSync(() => + observer.trigger([{ target: elementA, isIntersecting: true }]), + ); + + expect(observer.observedElements.has(elementB)).toBe(true); + expect(observer.observedElements.has(elementA)).toBe(false); + }); + test("skip pauses and resumes observation without losing state", () => { const rendered = render(SkipFixture); cleanup = rendered.cleanup;