Event-Reihenfolge korrigieren — focusin/focusout, Pointer-Sequenz, isTrusted #32

Closed
opened 2026-06-17 16:35:15 +00:00 by Artur · 0 comments
Owner

Problem

React Event-System erwartet exakte Event-Reihenfolge wie im realen Browser. Unsere Event-Dispatch-Engine (EventDispatcher + Interaction-API) hat drei Lücken:

1. Focus-Reihenfolge (React onFocus/onBlur)

Real Browser:

focusin (target: old) → focusout (target: new) → blur (old) → focus (new)

Unsere Engine:
focus() setzt nur document.activeElement + dispatht ein einzelnes FocusEvent. blur() analog. focusin/focusout fehlen oft oder sind falsch gereiht.

2. Pointer-Event-Sequenz (React onClick)

Real Browser:

pointerover → mouseover → pointerdown → mousedown → pointerup → mouseup → click

Unsere Engine (dispatch.ts):
dispatchClick() — Reihenfolge stimmt, aber PointerEvents haben teilweise fehlende Properties (pointerType, pressure, button).

3. isTrusted für alle dispatched Events

React 18+ prüft isTrusted in manchen Code-Pfaden. Wir setzen es via Object.defineProperty, aber nicht alle Events bekommen es.

Option A: dispatch.ts refactorn (Empfohlen)

Ein einzelner Fix-Durchlauf in src/interaction/dispatch.ts:

Focus-Reihenfolge fix

dispatchFocus(el: Element, relatedTarget: Element | null = null): void {
  // 1. Altes Element blur-Events dispatched
  const old = document.activeElement;
  if (old && old !== el) {
    dispatchFocusEvent(old, "focusout", { relatedTarget: el });
    dispatchFocusEvent(old, "blur", { relatedTarget: el });
  }
  // 2. Fokus setzen
  (el as HTMLElement).focus();
  // 3. Neues Element focus-Events dispatched
  dispatchFocusEvent(el, "focusin", { relatedTarget: old });
  dispatchFocusEvent(el, "focus", { relatedTarget: old });
}

Pointer-Properties fix

Sicherstellen dass PointerEvent-Properties pointerType, pressure, button, buttons, clientX/Y, pageX/Y, screenX/Y korrekt gesetzt sind.

isTrusted global setzen

Einmal im Dispatcher-Konstruktor oder als Wrapper:

function makeTrusted(event: Event): Event {
  Object.defineProperty(event, "isTrusted", { value: true, configurable: false });
  return event;
}

Option B: Event-Pipeline-Validator

Validator bauen, der alle dispatched Events gegen eine Referenz-Liste prüft (selbes Problem — mehr Code fürs gleiche Ergebnis).

Akzeptanzkriterien

  • focus(el) dispatht: focusout(old) → blur(old) → focusin(new) → focus(new)
  • blur(el) dispatht focusout/blur auf el
  • click(el) dispatht pointerover → mouseover → pointerdown → mousedown → pointerup → mouseup → click
  • PointerEvent hat pointerType, pressure, button, buttons
  • isTrusted === true auf ALLEN dispatched Events
  • relatedTarget ist korrekt gesetzt (alt↔neu bei focus/blur)
  • Bestehende Interaction-Tests bleiben grün

Betroffene Dateien

Datei Änderung
src/interaction/dispatch.ts Focus-Sequenz, Pointer-Props, isTrusted
tests/interaction/event-sequence.test.ts Neu: 20+ Tests

Tests

describe("Event Sequence", () => {
  // Focus
  it("focus dispatches focusout → blur → focusin → focus in order");
  it("blur dispatches focusout → blur");
  it("relatedTarget shows previous active element");
  it("relatedTarget is null when no previous focus");
  // Pointer
  it("click dispatches 7 events in correct order");
  it("dblclick dispatches click sequence twice");
  it("pointer events have pointerType");
  // isTrusted
  it("all dispatched events have isTrusted===true");
  it("isTrusted is non-writable");
  // Real browser alignment
  it("preventDefault during pointerdown blocks subsequent events");
  it("stopPropagation during pointerdown blocks subsequent events");
})

Cross-Referenzen

  • #30 MutationObserver-Batching: Focus-Events können MO auslösen
  • React 19 useActionState: Nutzt Form-Events + Focus-Management
## Problem React Event-System erwartet **exakte Event-Reihenfolge** wie im realen Browser. Unsere Event-Dispatch-Engine (EventDispatcher + Interaction-API) hat drei Lücken: ### 1. Focus-Reihenfolge (React onFocus/onBlur) Real Browser: ``` focusin (target: old) → focusout (target: new) → blur (old) → focus (new) ``` Unsere Engine: `focus()` setzt nur `document.activeElement` + dispatht ein einzelnes FocusEvent. `blur()` analog. `focusin`/`focusout` fehlen oft oder sind falsch gereiht. ### 2. Pointer-Event-Sequenz (React onClick) Real Browser: ``` pointerover → mouseover → pointerdown → mousedown → pointerup → mouseup → click ``` Unsere Engine (dispatch.ts): `dispatchClick()` — Reihenfolge stimmt, aber PointerEvents haben teilweise fehlende Properties (`pointerType`, `pressure`, `button`). ### 3. isTrusted für alle dispatched Events React 18+ prüft `isTrusted` in manchen Code-Pfaden. Wir setzen es via `Object.defineProperty`, aber nicht alle Events bekommen es. ## Option A: dispatch.ts refactorn (Empfohlen) Ein einzelner Fix-Durchlauf in `src/interaction/dispatch.ts`: ### Focus-Reihenfolge fix ```typescript dispatchFocus(el: Element, relatedTarget: Element | null = null): void { // 1. Altes Element blur-Events dispatched const old = document.activeElement; if (old && old !== el) { dispatchFocusEvent(old, "focusout", { relatedTarget: el }); dispatchFocusEvent(old, "blur", { relatedTarget: el }); } // 2. Fokus setzen (el as HTMLElement).focus(); // 3. Neues Element focus-Events dispatched dispatchFocusEvent(el, "focusin", { relatedTarget: old }); dispatchFocusEvent(el, "focus", { relatedTarget: old }); } ``` ### Pointer-Properties fix Sicherstellen dass PointerEvent-Properties `pointerType`, `pressure`, `button`, `buttons`, `clientX/Y`, `pageX/Y`, `screenX/Y` korrekt gesetzt sind. ### isTrusted global setzen Einmal im Dispatcher-Konstruktor oder als Wrapper: ```typescript function makeTrusted(event: Event): Event { Object.defineProperty(event, "isTrusted", { value: true, configurable: false }); return event; } ``` ## Option B: Event-Pipeline-Validator Validator bauen, der alle dispatched Events gegen eine Referenz-Liste prüft (selbes Problem — mehr Code fürs gleiche Ergebnis). ## Akzeptanzkriterien - [ ] `focus(el)` dispatht: focusout(old) → blur(old) → focusin(new) → focus(new) - [ ] `blur(el)` dispatht focusout/blur auf el - [ ] `click(el)` dispatht pointerover → mouseover → pointerdown → mousedown → pointerup → mouseup → click - [ ] PointerEvent hat pointerType, pressure, button, buttons - [ ] `isTrusted === true` auf ALLEN dispatched Events - [ ] `relatedTarget` ist korrekt gesetzt (alt↔neu bei focus/blur) - [ ] Bestehende Interaction-Tests bleiben grün ## Betroffene Dateien | Datei | Änderung | |-------|----------| | `src/interaction/dispatch.ts` | Focus-Sequenz, Pointer-Props, isTrusted | | `tests/interaction/event-sequence.test.ts` | **Neu:** 20+ Tests | ## Tests ```typescript describe("Event Sequence", () => { // Focus it("focus dispatches focusout → blur → focusin → focus in order"); it("blur dispatches focusout → blur"); it("relatedTarget shows previous active element"); it("relatedTarget is null when no previous focus"); // Pointer it("click dispatches 7 events in correct order"); it("dblclick dispatches click sequence twice"); it("pointer events have pointerType"); // isTrusted it("all dispatched events have isTrusted===true"); it("isTrusted is non-writable"); // Real browser alignment it("preventDefault during pointerdown blocks subsequent events"); it("stopPropagation during pointerdown blocks subsequent events"); }) ``` ## Cross-Referenzen - #30 MutationObserver-Batching: Focus-Events können MO auslösen - React 19 useActionState: Nutzt Form-Events + Focus-Management
Artur closed this issue 2026-06-17 16:43:35 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
glow-all/true-headless-browser#32
No description provided.