frontend: Touch-Langdruck springt beim Loslassen (Fokus blieb sonst nicht)

Auf echten Touch-Geräten flackerte die Markierung im Editor nach dem langen
Druck nur kurz auf und der Fokus fiel sofort wieder heraus.

Ursache: Der Sprung lief im 500-ms-Timer, und `focus()` aus einem
Timer-Callback gilt in mobilen Browsern nicht als Nutzergeste — ein Textfeld
darf sich so nicht selbst fokussieren (sonst könnte jede Seite ungefragt die
Bildschirmtastatur aufziehen). `touchend` ist eine Nutzergeste.

- Der Timer setzt jetzt nur noch die Rückmeldung: der Zielknoten bekommt die
  Klasse `armed` mit demselben Petrol-Ring wie unter dem Alt-Zeiger.
  `jumpToLine()` läuft im touchend-Handler; Wischen bricht weiterhin ab.
- Zweiter Beitrag zum selben Symptom: die native Textauswahl bzw. das
  Link-Callout des Browsers startet bei derselben Druckdauer und riss die
  Auswahl an sich. Drei Schichten dagegen, weil keine allein überall wirkt:
  contextmenu-preventDefault während des Drucks (überall), `user-select:none`
  an .node für grobe Zeiger (@media hover:none and pointer:coarse) und
  `-webkit-touch-callout:none` (nur iOS; Chrome kennt es nicht mehr). Die
  frühere Regel `a.node{-webkit-touch-callout:none}` geht darin auf.

Verifiziert (synthetische TouchEvents): während des Drucks nur `armed` +
Petrol-Ring, Auswahl noch unverändert, Fokus noch nicht im Textfeld; beim
Loslassen Sprung, Fokus TEXTAREA, Klick unterdrückt; Wischen und kurzer Druck
springen nicht und lassen den Klick durch. Ring greift auch unterhalb einer
any-of-Gruppe. Vitest 37/37.

Einschränkung: Weder die Nutzergesten-Regel noch die nativen Langdruck-Gesten
lassen sich synthetisch auslösen — der eigentliche Beweis ist ein Test auf
echter Hardware.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-07-27 13:41:48 +02:00
co-authored by Claude Opus 4.8
parent c496850b88
commit bbdecce75a
5 changed files with 66 additions and 16 deletions
+8 -2
View File
@@ -448,8 +448,14 @@
Rückmeldung, weil dort der einfache Klick dem Link gehört (D25). */
#out.alt .node{cursor:alias}
#out.alt .node:hover{box-shadow:0 0 0 2px var(--card),0 0 0 4px var(--or)}
/* Auf Touch ist der lange Druck die Sprung-Geste — kein iOS-Link-Callout. */
a.node{-webkit-touch-callout:none}
/* Langer Druck „scharf": derselbe Petrol-Ring wie unter dem Alt-Zeiger. */
#out .node.armed{box-shadow:0 0 0 2px var(--card),0 0 0 4px var(--or)}
/* Auf Touch ist der lange Druck die Sprung-Geste — die native Textauswahl und
das Link-Callout des Browsers starten zur selben Zeit und rissen sonst die
Auswahl im Textfeld wieder an sich (D25). */
@media (hover:none) and (pointer:coarse){
.node{-webkit-touch-callout:none;-webkit-user-select:none;user-select:none}
}
.ext{font-size:.72em;margin-left:5px;opacity:.55}
.size{
position:absolute;top:-9px;right:-9px;