fix(runtime): reach inner-child handlers via subtree descent (offscreen-safe clicks) - #35
Merged
Merged
Conversation
…en-safe clicks) clickElement routed its dispatch through document.elementFromPoint(centre) to land on the topmost node -- needed because a custom option often carries its handler on an inner child (jetblue: <jb-select-option> > div.body; a click on the outer node bubbles past the handler and no-ops). But elementFromPoint returns null for a point outside the viewport, so a far-scrolled/offscreen option fell back to the outer node and silently didn't commit. Replace it with deepestElementAt(root,x,y): descend the element's OWN subtree to the deepest node whose getBoundingClientRect contains the centre, then dispatch there so the event bubbles up to whichever inner node holds the handler. Own-rects are valid off-screen, so it reaches the inner child AND survives far-scroll -- no viewport dependency, no settle loop. The elementFromPoint variant is preserved as clickElementAtPoint (documented, unused) for the narrow hit-whatever-paints-on-top case. Validated live on jetblue's month/day/year/state/country selects (atomic, no scaffolding); 54 runtime tests green. Signed-off-by: Joel Webber <joel@fullstory.com>
The subtree-descent change to packages/runtime/src/dom.ts rebuilt the runtime bundle, but the committed copy embedded in the Go binary was not regenerated, so CI's 'Embedded runtime bundle in sync' check failed. Rebuild + re-embed via 'go generate ./runtimebundle/...'. Signed-off-by: Joel Webber <joel@fullstory.com>
joelgwebber
added a commit
that referenced
this pull request
Sep 11, 2026
…en-safe clicks) (#35) * fix(runtime): reach inner-child handlers via subtree descent (offscreen-safe clicks) clickElement routed its dispatch through document.elementFromPoint(centre) to land on the topmost node -- needed because a custom option often carries its handler on an inner child (jetblue: <jb-select-option> > div.body; a click on the outer node bubbles past the handler and no-ops). But elementFromPoint returns null for a point outside the viewport, so a far-scrolled/offscreen option fell back to the outer node and silently didn't commit. Replace it with deepestElementAt(root,x,y): descend the element's OWN subtree to the deepest node whose getBoundingClientRect contains the centre, then dispatch there so the event bubbles up to whichever inner node holds the handler. Own-rects are valid off-screen, so it reaches the inner child AND survives far-scroll -- no viewport dependency, no settle loop. The elementFromPoint variant is preserved as clickElementAtPoint (documented, unused) for the narrow hit-whatever-paints-on-top case. Validated live on jetblue's month/day/year/state/country selects (atomic, no scaffolding); 54 runtime tests green. Signed-off-by: Joel Webber <joel@fullstory.com> * chore(runtime): resync embedded runtime bundle The subtree-descent change to packages/runtime/src/dom.ts rebuilt the runtime bundle, but the committed copy embedded in the Go binary was not regenerated, so CI's 'Embedded runtime bundle in sync' check failed. Rebuild + re-embed via 'go generate ./runtimebundle/...'. Signed-off-by: Joel Webber <joel@fullstory.com> --------- Signed-off-by: Joel Webber <joel@fullstory.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
clickElementrouted its dispatch throughdocument.elementFromPoint(centre)toland on the topmost node — needed because a custom option often carries its
click handler on an inner child (JetBlue:
<jb-select-option> > div.body; a clickon the outer node bubbles past the handler and no-ops). But
elementFromPointreturns
nullfor a point outside the viewport, so a far-scrolled / offscreenoption fell back to the outer node and silently didn't commit.
Fix
Replace it with
deepestElementAt(root, x, y): descend the element's ownsubtree to the deepest node whose
getBoundingClientRectcontains the centre,then dispatch there so the event bubbles up to whichever inner node holds the
handler. Own-rects are valid off-screen, so it reaches the inner child and
survives far-scroll — no viewport dependency, no settle loop.
The
elementFromPointvariant is preserved asclickElementAtPoint(documented,unused) for the narrow "hit whatever paints on top at this point" case.
Validation
Live on JetBlue's month/day/year/state/country custom selects, driven atomically
with no scaffolding (including the exact far-scroll repro: day 28 → 3). Runtime
suite green (
click.test.tscovers subtree-descent reach, the offscreen guard,and the preserved
clickElementAtPoint).