UNPKG

tiny-event-intercept

Version:

Lightweight (~1.0KB) TypeScript library for conditional event interception with browser-standard API. Zero dependencies, SSR compatible, robust edge case handling.

74 lines (73 loc) 2.72 kB
const NOOP_CLEANUP = () => {}; function isValidEventTarget(target) { return target != null && typeof target === "object" && "addEventListener" in target && typeof target.addEventListener === "function" && "removeEventListener" in target && typeof target.removeEventListener === "function"; } function resolveTarget(target) { if (target === null) return null; if (typeof target === "function") try { return target(); } catch { return null; } return target; } function normalizeEvents(events) { return Array.isArray(events) ? events : [events]; } function isValidInterceptOptions(options) { return options != null && typeof options === "object" && "events" in options && "when" in options && typeof options.when === "function" && "listener" in options && typeof options.listener === "function"; } function interceptEvents(target, options) { if (typeof window === "undefined" || typeof document === "undefined") return NOOP_CLEANUP; if (!isValidInterceptOptions(options)) { console?.warn?.("interceptEvents: invalid options"); return NOOP_CLEANUP; } const resolvedTarget = resolveTarget(target) || document; if (!isValidEventTarget(resolvedTarget)) { console?.warn?.("interceptEvents: invalid target element"); return NOOP_CLEANUP; } const eventList = normalizeEvents(options.events); const validEvents = eventList.filter((eventType) => typeof eventType === "string" && eventType.trim().length > 0); if (validEvents.length === 0) { console?.warn?.("interceptEvents: no valid events provided"); return NOOP_CLEANUP; } const uniqueEvents = [...new Set(validEvents)]; const { when, listener } = options; const listenerOptions = { capture: options.capture ?? false, passive: options.passive ?? false, once: options.once ?? false, ...options.signal && { signal: options.signal } }; const listeners = new Map(); let isCleanedUp = false; uniqueEvents.forEach((eventType) => { const eventListener = (event) => { if (isCleanedUp) return; if (when()) listener(event); }; resolvedTarget.addEventListener(eventType, eventListener, listenerOptions); listeners.set(eventType, eventListener); }); const cleanup = () => { if (isCleanedUp) return; isCleanedUp = true; listeners.forEach((eventListener, eventType) => { resolvedTarget.removeEventListener(eventType, eventListener, listenerOptions); }); listeners.clear(); }; if (typeof window !== "undefined" && window.addEventListener) { const autoCleanup = () => cleanup(); window.addEventListener("beforeunload", autoCleanup, { once: true }); return () => { window.removeEventListener("beforeunload", autoCleanup); cleanup(); }; } return cleanup; } exports.interceptEvents = interceptEvents;