EventManager / createDoubleTapDetector

Eine an den Lebenszyklus gebundene Registrierung von Event-Listenern auf Basis von AbortController, dazu ein kleiner Doppeltipp-Erkenner für Touch-Gesten, dem die Zeigerart gleichgültig ist.

Das Problem, das hier gelöst wird, ist das Wieder-Abmelden von Listenern. removeEventListener greift nur, wenn du ihm exakt dieselbe Funktionsreferenz und dieselben Optionen übergibst, mit denen du registriert hast. Verpackst du den Handler beim Anmelden in eine Pfeilfunktion, bekommst du ihn nie wieder los. Komponenten, die immer wieder ein- und ausgehängt werden, verlieren dann pro Runde einen Listener. AbortController macht aus alldem ein einziges abort().

Verwendung

In einer Web Component

import { EventManager } from 'ranuts/utils';

class MyElement extends HTMLElement {
  private _events = new EventManager();

  connectedCallback() {
    this._events.on(this._input, 'input', this.handleInput).on(this, 'click', this.handleClick, { capture: true });
  }

  disconnectedCallback() {
    this._events.abort(); // meldet alle Listener ab und macht sich für das nächste Einhängen bereit
  }
}

In gewöhnlichem Seitencode

function initSection(container: HTMLElement) {
  const scope = new EventManager();

  scope.on(input, 'input', handleSearch).delegate(container, '[data-action]', 'click', (ev, target) => {
    handleAction(target.getAttribute('data-action'));
  });

  return () => scope.abort(); // aufrufen, wenn der Abschnitt abgebaut wird
}

API

on

Meldet einen Listener an, der an diesen Manager gebunden ist. Verkettbar.

Parameter

Parameter Beschreibung Typ Standard
target Das Ziel des Events EventTarget Erforderlich
type Name des Events string Erforderlich
handler Die Handler-Funktion EventListener Erforderlich
options Die Optionen von addEventListener ohne signal Omit<AddEventListenerOptions,'signal'> -

Rückgabe

Argument Beschreibung Typ
this Der Manager selbst, zum Verketten EventManager

delegate

Event-Delegation: hängt einen einzigen Listener an parent und ruft handler nur dann, wenn das Event von einem Nachfahren stammt, auf den selector passt. Verkettbar.

Der Handler bekommt das ursprüngliche Event und das getroffene Element.

scope.delegate(list, '.item', 'click', (ev, item) => {
  console.log(item.getAttribute('data-id'));
});

Parameter

Parameter Beschreibung Typ Standard
parent Das Element, an dem dieser eine Listener hängt HTMLElement Erforderlich
selector Der Selektor, auf den ein Nachfahre passen muss string Erforderlich
type Name des Events string Erforderlich
handler (event, matchedElement) => void Function Erforderlich
options Die Optionen von addEventListener ohne signal Omit<AddEventListenerOptions,'signal'> -

Rückgabe

Argument Beschreibung Typ
this Der Manager selbst, zum Verketten EventManager

abort

Meldet alle registrierten Listener ab und legt den internen AbortController neu an. Mehrfaches Aufrufen ist unbedenklich; spätere on()- und delegate()-Aufrufe beginnen in einem sauberen Geltungsbereich.

Rückgabe

Kein Rückgabewert (void)

signal

Das zugrunde liegende AbortSignal, falls du es selbst an addEventListener weiterreichen möchtest.

Argument Beschreibung Typ
signal Das Abbruchsignal des Managers AbortSignal

createDoubleTapDetector

Doppeltipp-Erkennung allein aus rohen (x, y, Zeit)-Werten. Die Zeigerart ist ihr gleichgültig, also arbeitet sie gleich, ob du sie aus Pointer-, Touch- oder Mouse-Events fütterst. Gedacht ist sie für Touch-Gesten – doppelt tippen zum Spulen, zum Zoomen, zum Liken –, wo die Logik aus Zeitfenster und Abstandsschwelle an jeder Stelle neu hergeleitet wird und dabei leicht unauffällig danebengeht: nur eine Achse verglichen, oder nach einem Treffer das Zurücksetzen vergessen, sodass drei schnelle Tipper als zwei überlappende Doppeltipps zählen.

import { createDoubleTapDetector } from 'ranuts/utils';

const detector = createDoubleTapDetector();
el.addEventListener('pointerup', (e) => {
  if (detector.check(e.clientX, e.clientY)) seek();
});

createDoubleTapDetector(options?)

Parameter (DoubleTapDetectorOptions)

Option Beschreibung Typ Standard
windowMs Größter erlaubter Abstand zwischen den beiden Tippern, in ms number 300
maxDistancePx Größte erlaubte Entfernung in der Fläche zwischen den beiden Tippern, in px number 60

DoubleTapDetector

Element Beschreibung Typ
check Vermerkt einen Tipp bei (x, y) und meldet, ob er zusammen mit dem unmittelbar davor einen Doppeltipp ergibt. Wird einer erkannt, beginnt die Erfassung von vorn, sodass ein dritter schneller Tipp ein neues Paar eröffnet, statt zum selben Doppeltipp zu zählen (x: number, y: number, now?: number) => boolean
reset Vergisst den zuletzt vermerkten Tipp; aufrufen, sobald eine andere Geste als ein Tipp beginnt (etwa ein Ziehen) () => void