From 04d408b29d059f131766448265768ce705801aaf Mon Sep 17 00:00:00 2001 From: Mathias Picker <48158184+MathiasWP@users.noreply.github.com> Date: Thu, 21 May 2026 21:53:34 +0200 Subject: [PATCH] perf: walk composedPath() directly in delegated event propagation (#18268) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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) --- .changeset/event-walk-composed-path.md | 5 +++++ .../src/internal/client/dom/elements/events.js | 15 +++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 .changeset/event-walk-composed-path.md diff --git a/.changeset/event-walk-composed-path.md b/.changeset/event-walk-composed-path.md new file mode 100644 index 0000000000..8b24573930 --- /dev/null +++ b/.changeset/event-walk-composed-path.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +perf: walk composedPath() directly in delegated event propagation diff --git a/packages/svelte/src/internal/client/dom/elements/events.js b/packages/svelte/src/internal/client/dom/elements/events.js index 5aa41e1c4d..45e042a8c6 100644 --- a/packages/svelte/src/internal/client/dom/elements/events.js +++ b/packages/svelte/src/internal/client/dom/elements/events.js @@ -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) {