Hacktoberfest 2026: as issues que os mantenedores marcaram para outubro, abertas e boas para iniciantes. Ver issues do Hacktoberfest

Anchor-click and form-submit navigations run outside the runtime's interaction frame (observe: NavigationEvent.interaction is undefined)

Fechada
#643 1 comentário 0 reações 0 responsáveis Ver no GitHub

Mantenedores costumam responder em até 1 dia

Ninguém assumiu esta issue ainda.

Avaliação

Dificuldade
4/5
Tempo estimado
3-5 dias
Facilidade para iniciantes
52/100
Tipo de issue
Bug
Clareza
Razoavelmente clara
Status de atividade
Ativa
Stack de tecnologia
typescript
Domínio
frontend, web-dev

Direção de pesquisa

Start in src/data/events.ts at setupNativeEvents, then inspect @solidjs/web's internal addEvent and the delegated event path through delegateEvents. Preserve the existing listener order and preload behavior; done means anchor clicks and native form submissions receive interaction attribution, preventDefault still works, and non-observe builds remain unchanged.

Escrita pelo modelo de indexação a partir do texto da issue.

Descrição

bug

Summary

Under Solid's observe tier, a navigation performed by clicking an <a> records no interaction: NavigationEvent.interaction is undefined, and the calls the route's data makes carry a navigation origin with no click behind it. A navigation performed by navigate() from an onClick handler is attributed correctly. The difference is where the handler runs: the router attaches its anchor handler to document directly, outside the web runtime's interaction frame.

Where

src/data/events.ts, setupNativeEvents:

// ensure delegated event run first
delegateEvents(["click", "submit"]);
document.addEventListener("click", handleAnchorClick);
if (preload) {
  document.addEventListener("mousemove", handleAnchorMove, { passive: true });
  document.addEventListener("focusin", handleAnchorPreload, { passive: true });
  document.addEventListener("touchstart", handleAnchorPreload, { passive: true });
}
document.addEventListener("submit", handleFormSubmit);

@solidjs/web wraps every handler it dispatches — delegated events via eventHandler, runtime-attached ones via addEvent — in dispatchAsInteraction, i.e. OBSERVE.attribution.withInteraction({ type, target, at }, fn), so every root write inside the handler (the router's location write included) is attributed to that interaction. A raw document.addEventListener goes around that wrapper. handleAnchorClick then calls navigateFromRoute, whose withOrigin(describeNavigation…) frame opens with no enclosing interaction, so the record is an orphan.

handleFormSubmit has the same shape: an action submitted through a native <form> is unattributed, while one submitted from a handler is not.

Observed

With @sentry/solid-2 consuming the records (e2e app on rc.13 / next.32, / → /users/6):

  • <button onClick={() => navigate('/users/6')}>: NavigationEvent.interaction is the click; the click span links to the route-named navigation span.
  • <a href="/users/6">: NavigationEvent.interaction === undefined; there is no click span at all, since no interaction frame ever opened. The navigation and its server-function call stand alone. The INP-relevant facts on the interaction record (inputDelayMs, handlerMs, settledMs) are lost for every link click in the app — which is most navigations.

The consumer documents the caveat for now rather than guessing a parent by time.

Cause

The direct document listeners date from the ordering requirement noted in the comment — Solid's delegated click must run first so a component's onClick with preventDefault() can stop the navigation — and predate the runtime having an interaction frame to run under. That ordering still has to hold; the fix is to run the handler inside the frame, not to change when it runs.

Options

  1. Router wraps its handlers. In setupNativeEvents, when OBSERVE is defined, dispatch handleAnchorClick and handleFormSubmit through OBSERVE.attribution.withInteraction({ type: evt.type, target: …, at: evt.timeStamp }, () => handler(evt)). Smallest change, but it duplicates the web runtime's describeEventTarget (element description, the values privacy gate for text) and interactionStart (the timeStamp clock check), which the router should not own.
  2. Web exposes the frame. @solidjs/web gains a small public entry — a listener attach that wraps under observe and is the identity function otherwise (addEvent(node, name, handler, false) already does exactly this but is marked @internal, compiler-emitted). The router uses it for the click and submit listeners. Preload listeners (mousemove, focusin, touchstart) stay raw: preloading is not a user interaction and should not open frames.
  3. Web stamps document-level listeners itself. Not viable without patching EventTarget.prototype; not proposed.

(2) is the right shape: the web runtime owns interaction description, the router only needs "run this as the event's handler". It is a public-surface addition on @solidjs/web, so it needs a decision there first; (1) can land in the router alone if that is preferred short-term.

Either way the order relative to Solid's delegated handler must not change: attach after delegateEvents(["click", "submit"]) exactly as today, so evt.defaultPrevented still reflects component handlers.

Acceptance

  • An anchor click that navigates produces a NavigationEvent whose interaction is the click's InteractionRef (type click, target a#…), the same object identity a navigate() from onClick gets today.
  • A <form> submitted to an action produces an interaction of type submit on the action's records.
  • A component onClick calling preventDefault() still stops the anchor navigation.
  • Nothing changes in non-observe builds (the wrapper folds to the plain listener).

Related: solidjs/solid#3683 (route declaration to the observe tier), getsentry/sentry-javascript#24517 (consumer; the caveat is documented in docs/solid-2-observe.md there).

— Claude via Cursor

Linguagem predominante
TypeScript
Estrelas
1.3k
Forks
183
Merge médio
14h 35min
PRs com merge (30d)
52

Preparar o ambiente

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Mais de solidjs/solid-router

Todas as issues de solidjs/solid-router

Issues semelhantes

Mais issues de TypeScript

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.