From 329e8c2f05ff8106f34b55bfe2b1e24026cfc37d Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 19 Apr 2024 14:17:36 +0200 Subject: [PATCH] fix: ensure event delegation stops after an error When a delegated event handler throws an error, ensure that delegated event handlers above it don't run. Strictly speaking this is different from attaching multiple separate event listeners because those above would still run, but it's impossible for us to simulate this behavior here. Fixes #8403 --- .changeset/calm-taxis-promise.md | 5 +++++ .../internal/client/dom/elements/events.js | 18 +++++++++++++----- .../samples/event-attribute-error/_config.js | 11 +++++++++++ .../samples/event-attribute-error/main.svelte | 19 +++++++++++++++++++ 4 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 .changeset/calm-taxis-promise.md create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-error/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-error/main.svelte diff --git a/.changeset/calm-taxis-promise.md b/.changeset/calm-taxis-promise.md new file mode 100644 index 000000000000..138e5a758c49 --- /dev/null +++ b/.changeset/calm-taxis-promise.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: ensure event delegation stops after an error diff --git a/packages/svelte/src/internal/client/dom/elements/events.js b/packages/svelte/src/internal/client/dom/elements/events.js index 330bcc266128..8e4fc93307ac 100644 --- a/packages/svelte/src/internal/client/dom/elements/events.js +++ b/packages/svelte/src/internal/client/dom/elements/events.js @@ -131,11 +131,19 @@ export function handle_event_propagation(handler_element, event) { var delegated = current_target[internal_prop_name]; if (delegated !== undefined && !(/** @type {any} */ (current_target).disabled)) { - if (is_array(delegated)) { - var [fn, ...data] = delegated; - fn.apply(current_target, [event, ...data]); - } else { - delegated.call(current_target, event); + try { + if (is_array(delegated)) { + var [fn, ...data] = delegated; + fn.apply(current_target, [event, ...data]); + } else { + delegated.call(current_target, event); + } + } catch (e) { + // @ts-expect-error ensure we don't run other handlers. Strictly speaking this is different + // from attaching multiple separate event listeners because those above would still run, + // but it's impossible for us to simulate this behavior here. + event.__root = document; + throw e; } } diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-error/_config.js b/packages/svelte/tests/runtime-runes/samples/event-attribute-error/_config.js new file mode 100644 index 000000000000..816afe8afe27 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-error/_config.js @@ -0,0 +1,11 @@ +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + const btn = target.querySelector('button'); + + await btn?.click(); + assert.htmlEqual(target.innerHTML, `
`); + }, + runtime_error: 'nope' +}); diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-error/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-attribute-error/main.svelte new file mode 100644 index 000000000000..978002edada2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-error/main.svelte @@ -0,0 +1,19 @@ + + +
+ +
+