perf: walk composedPath() directly in delegated event propagation (#18268)

## Summary

The propagation walk in `handle_event_propagation` already calls
`event.composedPath()` at the start to find the entry index, but then
re-derives the same chain step-by-step via `current_target.assignedSlot
|| current_target.parentNode || .host`. Three property reads per
iteration is measurable on the hot event path.

Walk the captured `path` array by index instead.

## Notes on behavior

`composedPath()` is the spec-compliant snapshot of the dispatch chain:
- Same shadow-DOM crossings (slots and shadow roots are included for
composed events).
- Same `host` traversal (composed-path crosses shadow boundaries when
appropriate).
- Differs from the previous walk in one edge case: if a handler removes
a parent mid-dispatch, the snapshot-based walk continues through the
captured chain (matches native browser semantics — the previous
`parentNode` walk would have stopped at a null parent).

## Performance

Measured in real Chromium on a click through a 30-deep tree with five
delegated handlers: **~245k hz → ~277k hz** (~+13%, ~−12% per-event
time).

## Test plan

- [x] All 6006 runtime tests pass (runtime-runes + runtime-legacy +
runtime-browser)
- [x] Native shadow-DOM event tests (in runtime-browser) pass unchanged

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pull/18271/head
Mathias Picker 3 months ago committed by GitHub
parent 078f901f61
commit 04d408b29d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
perf: walk composedPath() directly in delegated event propagation

@ -257,12 +257,7 @@ export function handle_event_propagation(event) {
var other_errors = [];
while (current_target !== null) {
/** @type {null | Element} */
var parent_element =
current_target.assignedSlot ||
current_target.parentNode ||
/** @type {any} */ (current_target).host ||
null;
if (current_target === handler_element) break;
try {
// @ts-expect-error
@ -284,10 +279,10 @@ export function handle_event_propagation(event) {
throw_error = error;
}
}
if (event.cancelBubble || parent_element === handler_element || parent_element === null) {
break;
}
current_target = parent_element;
if (event.cancelBubble) break;
path_idx++;
current_target = path_idx < path.length ? /** @type {Element} */ (path[path_idx]) : null;
}
if (throw_error) {

Loading…
Cancel
Save