Skip to content

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

ts
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

ts
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ámetroDescripciónTipoPor defecto
targetEl objetivo del eventoEventTargetObligatorio
typeNombre del eventostringObligatorio
handlerLa función manejadoraEventListenerObligatorio
optionsLas opciones de addEventListener menos signalOmit<AddEventListenerOptions,'signal'>-

Devuelve

ArgumentoDescripciónTipo
thisEl propio gestor, para encadenarEventManager

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ó.

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

Parámetros

ParámetroDescripciónTipoPor defecto
parentEl elemento al que se ata ese único escuchadorHTMLElementObligatorio
selectorEl selector con que debe casar el descendientestringObligatorio
typeNombre del eventostringObligatorio
handler(event, matchedElement) => voidFunctionObligatorio
optionsLas opciones de addEventListener menos signalOmit<AddEventListenerOptions,'signal'>-

Devuelve

ArgumentoDescripciónTipo
thisEl propio gestor, para encadenarEventManager

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.

ArgumentoDescripciónTipo
signalLa señal de cancelación del gestorAbortSignal

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.

ts
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ónDescripciónTipoPor defecto
windowMsSeparación máxima entre los dos toques, en msnumber300
maxDistancePxDistancia máxima en el plano entre los dos toques, en pxnumber60

DoubleTapDetector

MiembroDescripciónTipo
checkAnota 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
resetOlvida el último toque anotado; llámalo cuando empiece un gesto que no sea un toque (un arrastre)() => void

Publicado bajo la licencia MIT.