Skip to content

Commit 329e8c2

Browse files
committed
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
1 parent 307f15d commit 329e8c2

File tree

4 files changed

+48
-5
lines changed

4 files changed

+48
-5
lines changed

.changeset/calm-taxis-promise.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: ensure event delegation stops after an error

packages/svelte/src/internal/client/dom/elements/events.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,19 @@ export function handle_event_propagation(handler_element, event) {
131131
var delegated = current_target[internal_prop_name];
132132

133133
if (delegated !== undefined && !(/** @type {any} */ (current_target).disabled)) {
134-
if (is_array(delegated)) {
135-
var [fn, ...data] = delegated;
136-
fn.apply(current_target, [event, ...data]);
137-
} else {
138-
delegated.call(current_target, event);
134+
try {
135+
if (is_array(delegated)) {
136+
var [fn, ...data] = delegated;
137+
fn.apply(current_target, [event, ...data]);
138+
} else {
139+
delegated.call(current_target, event);
140+
}
141+
} catch (e) {
142+
// @ts-expect-error ensure we don't run other handlers. Strictly speaking this is different
143+
// from attaching multiple separate event listeners because those above would still run,
144+
// but it's impossible for us to simulate this behavior here.
145+
event.__root = document;
146+
throw e;
139147
}
140148
}
141149

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
async test({ assert, target }) {
5+
const btn = target.querySelector('button');
6+
7+
await btn?.click();
8+
assert.htmlEqual(target.innerHTML, `<div><button>1</button></div>`);
9+
},
10+
runtime_error: 'nope'
11+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script>
2+
let count = $state(0);
3+
4+
function yep() {
5+
count++;
6+
}
7+
8+
function nope() {
9+
count++;
10+
throw new Error('nope');
11+
}
12+
</script>
13+
14+
<div onclick={yep}>
15+
<button onclick={nope}>
16+
{count}
17+
</button>
18+
</div>
19+

0 commit comments

Comments
 (0)