@thednp/event-listener
Version:
🚅 Modern event listener for efficient web applications based on subscribe-publish pattern.
1 lines • 6.92 kB
Source Map (JSON)
{"version":3,"file":"event-listener.mjs","sources":["../src/index.ts"],"sourcesContent":["/**\n * Advanced event listener based on subscribe / publish pattern.\n *\n * @see https://www.patterns.dev/posts/classic-design-patterns/#observerpatternjavascript\n * @see https://gist.github.com/shystruk/d16c0ee7ac7d194da9644e5d740c8338#file-subpub-js\n * @see https://hackernoon.com/do-you-still-register-window-event-listeners-in-each-component-react-in-example-31a4b1f6f1c8\n */\nimport { version } from \"../package.json\";\n\nimport {\n AnimationEvent,\n AnimationEventHandler,\n ChangeEvent,\n ChangeEventHandler,\n ClipboardEvent,\n ClipboardEventHandler,\n CompositionEvent,\n CompositionEventHandler,\n DragEvent,\n DragEventHandler,\n EventRegistryEntry,\n EventsRegistry,\n FocusEvent,\n FocusEventHandler,\n FormEvent,\n FormEventHandler,\n KeyboardEvent,\n KeyboardEventHandler,\n MouseEvent,\n MouseEventHandler,\n NativeEvent,\n NativeEventHandler,\n PointerEvent,\n PointerEventHandler,\n PossibleEventTarget,\n TouchEvent,\n TouchEventHandler,\n TransitionEvent,\n TransitionEventHandler,\n UIEvent,\n UIEventHandler,\n WheelEvent,\n WheelEventHandler,\n} from \"./types\";\n\nconst registry: EventsRegistry = {};\n\n/**\n * The global event listener. This function must be a Function.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget\n */\nconst globalListener = (e: NativeEvent) => {\n const { type, currentTarget } = e;\n\n registry[type].forEach((listenersMap, element) => {\n /* istanbul ignore else @preserve */\n if (currentTarget === element) {\n listenersMap.forEach((options, listener) => {\n listener.apply(element, [e]);\n\n if (typeof options === \"object\" && options.once) {\n removeListener(element, type, listener, options);\n }\n });\n }\n });\n};\n\n/**\n * Register a new listener with its options and attach the `globalListener`\n * to the target if this is the first listener.\n */\nconst addListener = <T = Element, L = EventListener>(\n element: T,\n eventType: string,\n listener: L,\n options?: AddEventListenerOptions,\n): void => {\n // get element listeners first\n /* istanbul ignore else @preserve */\n if (!registry[eventType]) {\n registry[eventType] = new Map();\n }\n const oneEventMap = registry[eventType];\n\n /* istanbul ignore else @preserve */\n if (!oneEventMap.has(element as PossibleEventTarget)) {\n oneEventMap.set(element as PossibleEventTarget, new Map());\n }\n const oneElementMap = oneEventMap.get(\n element as PossibleEventTarget,\n ) as EventRegistryEntry<T, L>;\n\n // get listeners size\n const { size } = oneElementMap;\n\n // register listener with its options\n oneElementMap.set(listener, options);\n\n // add listener last\n /* istanbul ignore else @preserve */\n if (!size) {\n (element as PossibleEventTarget).addEventListener(\n eventType,\n globalListener as unknown as EventListener,\n options,\n );\n }\n};\n\n/**\n * Remove a listener from registry and detach the `globalListener`\n * if no listeners are found in the registry.\n */\nconst removeListener = <T = Element, L = EventListener>(\n element: T,\n eventType: string,\n listener: L,\n options?: AddEventListenerOptions,\n): void => {\n // get listener first\n const oneEventMap = registry[eventType];\n const oneElementMap = oneEventMap &&\n (oneEventMap.get(element as PossibleEventTarget) as EventRegistryEntry<T>);\n const savedOptions = oneElementMap &&\n oneElementMap.get(listener as NativeEventHandler<typeof element>);\n\n // also recover initial options\n const eventOptions = savedOptions !== undefined ? savedOptions : options;\n\n // unsubscribe second, remove from registry\n /* istanbul ignore else @preserve */\n if (\n oneElementMap &&\n oneElementMap.has(listener as NativeEventHandler<typeof element>)\n ) {\n oneElementMap.delete(listener as NativeEventHandler<typeof element>);\n }\n /* istanbul ignore else @preserve */\n if (oneEventMap && (!oneElementMap || !oneElementMap.size)) {\n oneEventMap.delete(element as PossibleEventTarget);\n }\n /* istanbul ignore else @preserve */\n if (!oneEventMap || !oneEventMap.size) delete registry[eventType];\n\n // remove listener last\n /* istanbul ignore else @preserve */\n if (!oneElementMap || !oneElementMap.size) {\n (element as PossibleEventTarget).removeEventListener(\n eventType,\n globalListener as unknown as EventListener,\n eventOptions,\n );\n }\n};\n\n// alias main methods\nconst on: typeof addListener = addListener;\nconst off: typeof removeListener = removeListener;\n\nexport {\n addListener,\n globalListener,\n off,\n on,\n registry,\n removeListener,\n version,\n};\nexport type {\n AnimationEvent,\n AnimationEventHandler,\n ChangeEvent,\n ChangeEventHandler,\n ClipboardEvent,\n ClipboardEventHandler,\n CompositionEvent,\n CompositionEventHandler,\n DragEvent,\n DragEventHandler,\n EventRegistryEntry,\n EventsRegistry,\n FocusEvent,\n FocusEventHandler,\n FormEvent,\n FormEventHandler,\n KeyboardEvent,\n KeyboardEventHandler,\n MouseEvent,\n MouseEventHandler,\n NativeEvent,\n NativeEventHandler,\n PointerEvent,\n PointerEventHandler,\n PossibleEventTarget,\n TouchEvent,\n TouchEventHandler,\n TransitionEvent,\n TransitionEventHandler,\n UIEvent,\n UIEventHandler,\n WheelEvent,\n WheelEventHandler,\n};\n"],"names":["registry","globalListener","e","type","currentTarget","listenersMap","element","options","listener","removeListener","addListener","eventType","oneEventMap","oneElementMap","size","savedOptions","eventOptions","on","off"],"mappings":"oBAuCMA,IAA2B,CAAA,GAG3BC,IAAiB,CAACC,MAAmB;AACnC,QAAA,EAAE,MAAAC,GAAM,eAAAC,EAAA,IAAkBF;AAEhC,EAAAF,EAASG,CAAI,EAAE,QAAQ,CAACE,GAAcC,MAAY;AAEhD,IAAIF,MAAkBE,KACPD,EAAA,QAAQ,CAACE,GAASC,MAAa;AAC1C,MAAAA,EAAS,MAAMF,GAAS,CAACJ,CAAC,CAAC,GAEvB,OAAOK,KAAY,YAAYA,EAAQ,QAC1BE,EAAAH,GAASH,GAAMK,GAAUD,CAAO;AAAA,IACjD,CACD;AAAA,EACH,CACD;AACH,GAGMG,IAAc,CAClBJ,GACAK,GACAH,GACAD,MACS;AAGL,EAACP,EAASW,CAAS,MACZX,EAAAW,CAAS,IAAI,oBAAI,IAAI;AAE1B,QAAAC,IAAcZ,EAASW,CAAS;AAGtC,EAAKC,EAAY,IAAIN,CAA8B,KACjDM,EAAY,IAAIN,GAAoC,oBAAA,IAAA,CAAK;AAE3D,QAAMO,IAAgBD,EAAY;AAAA,IAChCN;AAAA,EACF,GAGM,EAAE,MAAAQ,MAASD;AAGH,EAAAA,EAAA,IAAIL,GAAUD,CAAO,GAI9BO,KACFR,EAAgC;AAAA,IAC/BK;AAAA,IACAV;AAAA,IACAM;AAAA,EACF;AAEJ,GAGME,IAAiB,CACrBH,GACAK,GACAH,GACAD,MACS;AAEH,QAAAK,IAAcZ,EAASW,CAAS,GAChCE,IAAgBD,KACnBA,EAAY,IAAIN,CAA8B,GAC3CS,IAAeF,KACnBA,EAAc,IAAIL,CAA8C,GAG5DQ,IAAeD,MAAiB,SAAYA,IAAeR;AAIjE,EACEM,KACAA,EAAc,IAAIL,CAA8C,KAEhEK,EAAc,OAAOL,CAA8C,GAGjEI,MAAgB,CAACC,KAAiB,CAACA,EAAc,SACnDD,EAAY,OAAON,CAA8B,IAG/C,CAACM,KAAe,CAACA,EAAY,SAAM,OAAOZ,EAASW,CAAS,IAI5D,CAACE,KAAiB,CAACA,EAAc,SAClCP,EAAgC;AAAA,IAC/BK;AAAA,IACAV;AAAA,IACAe;AAAA,EACF;AAEJ,GAGMC,IAAyBP,GACzBQ,IAA6BT;"}