From 214dd3d005e78c90d6c93e76a07b801303f53b53 Mon Sep 17 00:00:00 2001 From: "svelte-triage-bot[bot]" <316883489+svelte-triage-bot[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 08:38:45 +0200 Subject: [PATCH] fix: cancel deferred event listener attachment (#18749) Fixes #18748 --- .changeset/calm-events-cleanup.md | 5 +++++ .../internal/client/dom/elements/events.js | 7 ++++++- .../Child.svelte | 1 + .../_config.js | 8 +++++++ .../main.svelte | 21 +++++++++++++++++++ 5 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 .changeset/calm-events-cleanup.md create mode 100644 packages/svelte/tests/runtime-runes/samples/event-cleanup-before-attachment/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/event-cleanup-before-attachment/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/event-cleanup-before-attachment/main.svelte diff --git a/.changeset/calm-events-cleanup.md b/.changeset/calm-events-cleanup.md new file mode 100644 index 0000000000..4f5c2357f1 --- /dev/null +++ b/.changeset/calm-events-cleanup.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: cancel deferred event listeners during cleanup diff --git a/packages/svelte/src/internal/client/dom/elements/events.js b/packages/svelte/src/internal/client/dom/elements/events.js index 068c64b9ab..0ed2ffc085 100644 --- a/packages/svelte/src/internal/client/dom/elements/events.js +++ b/packages/svelte/src/internal/client/dom/elements/events.js @@ -78,8 +78,11 @@ export function create_event(event_name, dom, handler, options = {}) { event_name.startsWith('touch') || event_name === 'wheel' ) { + target_handler.__removed = false; queue_micro_task(() => { - dom.addEventListener(event_name, target_handler, options); + if (!target_handler.__removed) { + dom.addEventListener(event_name, target_handler, options); + } }); } else { dom.addEventListener(event_name, target_handler, options); @@ -102,6 +105,7 @@ export function on(element, type, handler, options = {}) { var target_handler = create_event(type, element, handler, options); return () => { + target_handler.__removed = true; element.removeEventListener(type, target_handler, options); }; } @@ -128,6 +132,7 @@ export function event(event_name, dom, handler, capture, passive) { dom instanceof HTMLMediaElement ) { teardown(() => { + target_handler.__removed = true; dom.removeEventListener(event_name, target_handler, options); }); } diff --git a/packages/svelte/tests/runtime-runes/samples/event-cleanup-before-attachment/Child.svelte b/packages/svelte/tests/runtime-runes/samples/event-cleanup-before-attachment/Child.svelte new file mode 100644 index 0000000000..98e09b7a20 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-cleanup-before-attachment/Child.svelte @@ -0,0 +1 @@ + console.log('template')} /> diff --git a/packages/svelte/tests/runtime-runes/samples/event-cleanup-before-attachment/_config.js b/packages/svelte/tests/runtime-runes/samples/event-cleanup-before-attachment/_config.js new file mode 100644 index 0000000000..0e29a46e36 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-cleanup-before-attachment/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + async test({ assert, logs }) { + await new Promise((resolve) => setTimeout(resolve)); + assert.deepEqual(logs, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/event-cleanup-before-attachment/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-cleanup-before-attachment/main.svelte new file mode 100644 index 0000000000..d5a8f1a201 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-cleanup-before-attachment/main.svelte @@ -0,0 +1,21 @@ + + +