diff --git a/.changeset/upset-spiders-study.md b/.changeset/upset-spiders-study.md new file mode 100644 index 0000000000..3f90828038 --- /dev/null +++ b/.changeset/upset-spiders-study.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: preserve delegated event handlers as long as one or more root components are using them diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index c09f5fdd05..0d5bc6cb49 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -149,8 +149,8 @@ export function hydrate(component, options) { } } -/** @type {Map} */ -const document_listeners = new Map(); +/** @type {Map>} */ +const listeners = new Map(); /** * @template {Record} Exports @@ -177,17 +177,25 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro // Add the event listener to both the container and the document. // The container listener ensures we catch events from within in case // the outer content stops propagation of the event. - target.addEventListener(event_name, handle_event_propagation, { passive }); + // + // The document listener ensures we catch events that originate from elements that were + // manually moved outside of the container (e.g. via manual portals). + for (const node of [target, document]) { + var counts = listeners.get(node); + + if (counts === undefined) { + counts = new Map(); + listeners.set(node, counts); + } - var n = document_listeners.get(event_name); + var count = counts.get(event_name); - if (n === undefined) { - // The document listener ensures we catch events that originate from elements that were - // manually moved outside of the container (e.g. via manual portals). - document.addEventListener(event_name, handle_event_propagation, { passive }); - document_listeners.set(event_name, 1); - } else { - document_listeners.set(event_name, n + 1); + if (count === undefined) { + node.addEventListener(event_name, handle_event_propagation, { passive }); + counts.set(event_name, 1); + } else { + counts.set(event_name, count + 1); + } } } }; @@ -245,15 +253,20 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro return () => { for (var event_name of registered_events) { - target.removeEventListener(event_name, handle_event_propagation); - - var n = /** @type {number} */ (document_listeners.get(event_name)); - - if (--n === 0) { - document.removeEventListener(event_name, handle_event_propagation); - document_listeners.delete(event_name); - } else { - document_listeners.set(event_name, n); + for (const node of [target, document]) { + var counts = /** @type {Map} */ (listeners.get(node)); + var count = /** @type {number} */ (counts.get(event_name)); + + if (--count == 0) { + node.removeEventListener(event_name, handle_event_propagation); + counts.delete(event_name); + + if (counts.size === 0) { + listeners.delete(node); + } + } else { + counts.set(event_name, count); + } } }