diff --git a/.changeset/light-penguins-invent.md b/.changeset/light-penguins-invent.md new file mode 100644 index 000000000000..2610fddf069a --- /dev/null +++ b/.changeset/light-penguins-invent.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow events to continue propagating following 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..536b1c044244 100644 --- a/packages/svelte/src/internal/client/dom/elements/events.js +++ b/packages/svelte/src/internal/client/dom/elements/events.js @@ -122,36 +122,42 @@ export function handle_event_propagation(handler_element, event) { } }); - while (current_target !== null) { + /** @param {Element} current_target */ + function next(current_target) { /** @type {null | Element} */ var parent_element = current_target.parentNode || /** @type {any} */ (current_target).host || null; - var internal_prop_name = '__' + event_name; - // @ts-ignore - 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); - } - } - if ( - event.cancelBubble || - parent_element === handler_element || - current_target === handler_element - ) { - break; + try { + // @ts-expect-error + var delegated = current_target['__' + event_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); + } + } + } finally { + if ( + !event.cancelBubble && + parent_element !== handler_element && + parent_element !== null && + current_target !== handler_element + ) { + next(parent_element); + } } - - current_target = parent_element; } - // @ts-expect-error is used above - event.__root = handler_element; - // @ts-expect-error is used above - current_target = handler_element; + try { + next(current_target); + } finally { + // @ts-expect-error is used above + event.__root = handler_element; + // @ts-expect-error is used above + current_target = handler_element; + } } diff --git a/packages/svelte/tests/runtime-runes/samples/event-propagation-with-error/_config.js b/packages/svelte/tests/runtime-runes/samples/event-propagation-with-error/_config.js new file mode 100644 index 000000000000..d12f42f8634c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-propagation-with-error/_config.js @@ -0,0 +1,15 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: `
`, + + async test({ assert, target }) { + const button1 = target.querySelector('button'); + + flushSync(() => button1?.click()); + assert.htmlEqual(target.innerHTML, `
`); + }, + + runtime_error: 'nope' +}); diff --git a/packages/svelte/tests/runtime-runes/samples/event-propagation-with-error/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-propagation-with-error/main.svelte new file mode 100644 index 000000000000..4bab3363479d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-propagation-with-error/main.svelte @@ -0,0 +1,19 @@ + + +
+ +