EventManager / createDoubleTapDetector

Un registro de eventos atado al ciclo de vida y apoyado en AbortController, más un pequeño detector de doble toque, indiferente al tipo de puntero, para gestos táctiles.

El problema que resuelve es volver a quitar los escuchadores. removeEventListener solo funciona si le entregas exactamente la misma referencia de función y las mismas opciones con que lo registraste. Envuelve el manejador en una función flecha al pasarlo y ya no podrás quitarlo nunca. Los componentes que se montan y desmontan una y otra vez van dejando escapar un escuchador por ciclo. AbortController convierte todo eso en un único abort().

Uso

Dentro de un 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(); // quita todos los escuchadores y deja el terreno limpio para la próxima conexión
  }
}

En el código normal de una página

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(); // llámalo cuando se desmonte la sección
}

API

on

Registra un escuchador atado a este gestor. Se puede encadenar.

Parámetros

Parámetro Descripción Tipo Por defecto
target El objetivo del evento EventTarget Obligatorio
type Nombre del evento string Obligatorio
handler La función manejadora EventListener Obligatorio
options Las opciones de addEventListener menos signal Omit<AddEventListenerOptions,'signal'> -

Devuelve

Argumento Descripción Tipo
this El propio gestor, para encadenar EventManager

delegate

Delegación de eventos: pone un solo escuchador en parent y llama a handler únicamente cuando el evento nació de un descendiente que casa con selector. Se puede encadenar.

El manejador recibe el evento original y el elemento que casó.

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

Parámetros

Parámetro Descripción Tipo Por defecto
parent El elemento al que se ata ese único escuchador HTMLElement Obligatorio
selector El selector con que debe casar el descendiente string Obligatorio
type Nombre del evento string Obligatorio
handler (event, matchedElement) => void Function Obligatorio
options Las opciones de addEventListener menos signal Omit<AddEventListenerOptions,'signal'> -

Devuelve

Argumento Descripción Tipo
this El propio gestor, para encadenar EventManager

abort

Quita todos los escuchadores registrados y renueva el AbortController interno. Se puede llamar más de una vez sin problema; las llamadas posteriores a on() o delegate() parten de un ámbito limpio.

Devuelve

Sin valor de retorno (void)

signal

El AbortSignal subyacente, por si quieres pasárselo tú a addEventListener.

Argumento Descripción Tipo
signal La señal de cancelación del gestor AbortSignal

createDoubleTapDetector

Detección de doble toque a partir de muestras crudas de (x, y, tiempo). Es indiferente al tipo de puntero, así que funciona igual la alimentes con eventos Pointer, Touch o Mouse. Está pensada para gestos táctiles (doble toque para avanzar, para hacer zoom, para dar un «me gusta»), donde rehacer en cada sitio la lógica del umbral de tiempo y distancia es fácil de equivocar de forma sutil: comparar un solo eje, u olvidar reiniciar tras un acierto y que tres toques rápidos cuenten como dos dobles toques solapados.

import { createDoubleTapDetector } from 'ranuts/utils';

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

createDoubleTapDetector(options?)

Parámetros (DoubleTapDetectorOptions)

Opción Descripción Tipo Por defecto
windowMs Separación máxima entre los dos toques, en ms number 300
maxDistancePx Distancia máxima en el plano entre los dos toques, en px number 60

DoubleTapDetector

Miembro Descripción Tipo
check Anota un toque en (x, y) y dice si forma un doble toque con el inmediatamente anterior. Al detectar uno, el seguimiento se reinicia, de modo que un tercer toque rápido empieza una pareja nueva en vez de contar como parte del mismo doble toque (x: number, y: number, now?: number) => boolean
reset Olvida el último toque anotado; llámalo cuando empiece un gesto que no sea un toque (un arrastre) () => void