EventManager / createDoubleTapDetector

AbortController를 바탕으로 생명주기에 묶어 두는 이벤트 등록기, 그리고 포인터 종류를 가리지 않는 작은 더블 탭 감지기를 담았습니다.

이 도구가 푸는 문제는 붙여 둔 리스너를 다시 떼어내는 일 입니다. removeEventListener는 등록할 때 넘긴 것과 완전히 같은 함수 참조와 옵션을 건네야만 동작합니다. 넘기는 길에 핸들러를 화살표 함수로 감싸면 그 뒤로는 영영 떼어낼 수 없습니다. 그러면 붙었다 떨어졌다를 반복하는 컴포넌트는 한 주기마다 리스너를 하나씩 흘리게 됩니다. AbortController는 그 모든 것을 abort() 한 번으로 바꿔 줍니다.

사용법

웹 컴포넌트 안에서

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(); // 리스너를 모두 떼고, 다음 연결을 위해 상태를 되돌립니다
  }
}

평범한 페이지 코드에서

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(); // 해당 영역을 해제할 때 호출하세요
}

API

on

이 매니저에 묶인 리스너를 등록합니다. 체이닝할 수 있습니다.

매개변수

매개변수 설명 타입 기본값
target 이벤트 대상 EventTarget 필수
type 이벤트 이름 string 필수
handler 핸들러 함수 EventListener 필수
options signal을 뺀 addEventListener 옵션 Omit<AddEventListenerOptions,'signal'> -

반환값

인자 설명 타입
this 체이닝을 위한 매니저 자신 EventManager

delegate

이벤트 위임입니다. parent에 리스너를 하나만 붙이고, selector에 맞는 자손에서 시작된 이벤트일 때만 handler를 부릅니다. 체이닝할 수 있습니다.

핸들러는 원래 이벤트와 조건에 맞은 요소를 함께 받습니다.

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

매개변수

매개변수 설명 타입 기본값
parent 그 하나뿐인 리스너를 붙일 요소 HTMLElement 필수
selector 자손이 만족해야 할 선택자 string 필수
type 이벤트 이름 string 필수
handler (event, matchedElement) => void Function 필수
options signal을 뺀 addEventListener 옵션 Omit<AddEventListenerOptions,'signal'> -

반환값

인자 설명 타입
this 체이닝을 위한 매니저 자신 EventManager

abort

등록된 리스너를 모두 떼고 내부 AbortController를 새로 만듭니다. 여러 번 불러도 안전하며, 이후의 on() · delegate() 호출은 깨끗한 범위에서 다시 시작합니다.

반환값

반환값 없음(void)

signal

직접 addEventListener에 넘기고 싶을 때 쓰는, 바탕이 되는 AbortSignal입니다.

인자 설명 타입
signal 매니저의 중단 시그널 AbortSignal

createDoubleTapDetector

가공하지 않은 (x, y, 시각) 표본만으로 더블 탭을 감지합니다. 포인터 종류를 가리지 않으므로 Pointer·Touch·Mouse 어느 이벤트를 먹여도 똑같이 동작합니다. 두 번 탭해 구간을 옮기거나 확대하거나 좋아요를 누르는 식의 터치 제스처를 겨냥해 만들었습니다. 이런 곳에서 시각과 거리 임계값 계산을 매번 다시 짜면 티 나지 않게 어긋나기 쉽습니다. 한 축만 비교한다든지, 성공한 뒤 초기화를 잊어 빠른 탭 세 번이 겹치는 더블 탭 두 번으로 세어진다든지 하는 식으로요.

import { createDoubleTapDetector } from 'ranuts/utils';

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

createDoubleTapDetector(options?)

매개변수 (DoubleTapDetectorOptions)

옵션 설명 타입 기본값
windowMs 두 탭 사이에 허용할 최대 간격(밀리초) number 300
maxDistancePx 두 탭 사이에 허용할 평면상 최대 거리(픽셀) number 60

DoubleTapDetector

멤버 설명 타입
check (x, y) 지점의 탭을 기록하고, 바로 앞 탭과 묶여 더블 탭이 되는지 알려 줍니다. 더블 탭이 잡히면 추적이 초기화되므로, 세 번째 빠른 탭은 같은 더블 탭의 일부가 아니라 새 짝의 시작이 됩니다 (x: number, y: number, now?: number) => boolean
reset 마지막으로 기록한 탭을 잊습니다. 탭이 아닌 제스처(드래그 등)가 시작될 때 부르세요 () => void