🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
Book a DemoInstallSign in
Socket

@thednp/event-listener

Package Overview
Dependencies
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@thednp/event-listener - npm Package Compare versions

Comparing version

to
2.0.3

src/types.ts

185

dist/event-listener.d.ts
/**
* Advanced event listener based on subscribe / publish pattern.
* Type definitions addapted from React 18.2
* Project: https://react.dev/
*/
export type NativeAnimationEvent = AnimationEvent;
export type NativeClipboardEvent = ClipboardEvent;
export type NativeCompositionEvent = CompositionEvent;
export type NativeDragEvent = DragEvent;
export type NativeFocusEvent = FocusEvent;
export type NativeKeyboardEvent = KeyboardEvent;
export type NativeMouseEvent = MouseEvent;
export type NativeTouchEvent = TouchEvent;
export type NativePointerEvent = PointerEvent;
export type NativeTransitionEvent = TransitionEvent;
export type NativeUIEvent = UIEvent;
export type NativeWheelEvent = WheelEvent;
export interface AbstractView {
styleMedia: StyleMedia;
document: Document;
}
export interface BaseEvent<E = unknown, C = unknown, T = unknown> {
nativeEvent: Event & E;
currentTarget: C & EventTarget;
target: T & EventTarget;
bubbles: boolean;
cancelable: boolean;
defaultPrevented: boolean;
eventPhase: number;
isTrusted: boolean;
preventDefault(): void;
isDefaultPrevented(): boolean;
stopPropagation(): void;
isPropagationStopped(): boolean;
persist(): void;
timeStamp: number;
type: string & NativeEventTypes;
}
/**
* currentTarget - a reference to the element on which the event listener is registered.
*
* @see https://www.patterns.dev/posts/classic-design-patterns/#observerpatternjavascript
* @see https://gist.github.com/shystruk/d16c0ee7ac7d194da9644e5d740c8338#file-subpub-js
* @see https://hackernoon.com/do-you-still-register-window-event-listeners-in-each-component-react-in-example-31a4b1f6f1c8
* target - a reference to the element from which the event was originally dispatched.
* This might be a child element to the element on which the event listener is registered.
* If you thought this should be `EventTarget & T`, see https://github.com/DefinitelyTyped/DefinitelyTyped/issues/11508#issuecomment-256045682
*/
export type ListenerObject = Map<EventListener, AddEventListenerOptions | undefined | boolean>;
export type EventsRegistry = Record<string, Map<EventTarget, ListenerObject>>;
export type NativeEvent<T = PossibleEventTarget, E = Event> = BaseEvent<E, EventTarget & T, EventTarget>;
export interface ClipboardEvent<T = Element> extends NativeEvent<T, NativeClipboardEvent> {
clipboardData?: DataTransfer;
}
export interface CompositionEvent<T = Element> extends NativeEvent<T, NativeCompositionEvent> {
data: string;
}
export interface DragEvent<T = Element> extends MouseEvent<T, NativeDragEvent> {
dataTransfer: DataTransfer;
}
export interface PointerEvent<T = Element> extends MouseEvent<T, NativePointerEvent> {
pointerId: number;
pressure: number;
tangentialPressure: number;
tiltX: number;
tiltY: number;
twist: number;
width: number;
height: number;
pointerType: "mouse" | "pen" | "touch";
isPrimary: boolean;
}
export interface FocusEvent<T = Element, R = Element> extends NativeEvent<T, NativeFocusEvent> {
relatedTarget: (EventTarget & R) | null;
target: EventTarget & T;
}
export type FormControl = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
export type FormEvent<T = FormControl> = NativeEvent<T>;
export interface ChangeEvent<T = FormControl> extends FormEvent<T> {
target: EventTarget & T;
}
export type ModifierKey = "Alt" | "AltGraph" | "CapsLock" | "Control" | "Fn" | "FnLock" | "Hyper" | "Meta" | "NumLock" | "ScrollLock" | "Shift" | "Super" | "Symbol" | "SymbolLock";
export interface KeyboardEvent<T = Element> extends UIEvent<T, NativeKeyboardEvent> {
altKey: boolean;
/** @deprecated */
charCode: number;
ctrlKey: boolean;
code: string;
/**
* See [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#keys-modifier). for a list of valid (case-sensitive) arguments to this method.
*/
getModifierState(key: ModifierKey): boolean;
/**
* See the [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#named-key-attribute-values). for possible values
*/
key: string;
/** @deprecated */
keyCode: number;
locale: string;
location: number;
metaKey: boolean;
repeat: boolean;
shiftKey: boolean;
/** @deprecated */
which: number;
}
export interface MouseEvent<T = Element, E = NativeMouseEvent> extends UIEvent<T, E> {
altKey: boolean;
button: number;
buttons: number;
clientX: number;
clientY: number;
ctrlKey: boolean;
/**
* See [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#keys-modifier). for a list of valid (case-sensitive) arguments to this method.
*/
getModifierState(key: ModifierKey): boolean;
metaKey: boolean;
movementX: number;
movementY: number;
pageX: number;
pageY: number;
relatedTarget: EventTarget | null;
screenX: number;
screenY: number;
shiftKey: boolean;
}
export interface TouchEvent<T = Element> extends UIEvent<T, NativeTouchEvent> {
altKey: boolean;
changedTouches: TouchList;
ctrlKey: boolean;
/**
* See [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#keys-modifier). for a list of valid (case-sensitive) arguments to this method.
*/
getModifierState(key: ModifierKey): boolean;
metaKey: boolean;
shiftKey: boolean;
targetTouches: TouchList;
touches: TouchList;
}
export interface UIEvent<T = Element, E = NativeUIEvent> extends NativeEvent<T, E> {
detail: number;
view: AbstractView;
}
export interface WheelEvent<T = Element> extends MouseEvent<T, NativeWheelEvent> {
deltaMode: number;
deltaX: number;
deltaY: number;
deltaZ: number;
}
export interface AnimationEvent<T = Element> extends NativeEvent<T, NativeAnimationEvent> {
animationName: string;
elapsedTime: number;
pseudoElement: string;
}
export interface TransitionEvent<T = Element> extends NativeEvent<T, NativeTransitionEvent> {
elapsedTime: number;
propertyName: string;
pseudoElement: string;
}
export type EventHandler<E extends NativeEvent<unknown, unknown>> = (event: E) => void;
export type NativeEventHandler<T = Element> = EventHandler<NativeEvent<T>>;
export type ClipboardEventHandler<T = Element> = EventHandler<ClipboardEvent<T>>;
export type CompositionEventHandler<T = Element> = EventHandler<CompositionEvent<T>>;
export type DragEventHandler<T = Element> = EventHandler<DragEvent<T>>;
export type FocusEventHandler<T = Element> = EventHandler<FocusEvent<T>>;
export type FormEventHandler<T = Element> = EventHandler<FormEvent<T>>;
export type ChangeEventHandler<T = Element> = EventHandler<ChangeEvent<T>>;
export type KeyboardEventHandler<T = Element> = EventHandler<KeyboardEvent<T>>;
export type MouseEventHandler<T = Element> = EventHandler<MouseEvent<T>>;
export type TouchEventHandler<T = Element> = EventHandler<TouchEvent<T>>;
export type PointerEventHandler<T = Element> = EventHandler<PointerEvent<T>>;
export type UIEventHandler<T = Element> = EventHandler<UIEvent<T>>;
export type WheelEventHandler<T = Element> = EventHandler<WheelEvent<T>>;
export type AnimationEventHandler<T = Element> = EventHandler<AnimationEvent<T>>;
export type TransitionEventHandler<T = Element> = EventHandler<TransitionEvent<T>>;
export type SupportedEventObject<T> = NativeEvent<T> | ClipboardEvent<T> | CompositionEvent<T> | DragEvent<T> | FocusEvent<T> | FormEvent<T> | ChangeEvent<T> | KeyboardEvent<T> | MouseEvent<T> | TouchEvent<T> | PointerEvent<T> | UIEvent<T> | WheelEvent<T> | AnimationEvent<T> | TransitionEvent<T>;
export type SupportedEventHandler<T> = NativeEventHandler<T> | ClipboardEventHandler<T> | CompositionEventHandler<T> | DragEventHandler<T> | FocusEventHandler<T> | FormEventHandler<T> | ChangeEventHandler<T> | KeyboardEventHandler<T> | MouseEventHandler<T> | TouchEventHandler<T> | PointerEventHandler<T> | UIEventHandler<T> | WheelEventHandler<T> | AnimationEventHandler<T> | TransitionEventHandler<T>;
export type NativeEventTypes = "DOMContentLoaded" | "DOMMouseScroll" | "abort" | "beforeunload" | "blur" | "change" | "click" | "contextmenu" | "dblclick" | "error" | "focus" | "focusin" | "focusout" | "gesturechange" | "gestureend" | "gesturestart" | "hover" | "keydown" | "keypress" | "keyup" | "load" | "mousedown" | "mouseenter" | "mousein" | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" | "mousewheel" | "move" | "orientationchange" | "pointercancel" | "pointerdown" | "pointerleave" | "pointermove" | "pointerup" | "readystatechange" | "reset" | "resize" | "scroll" | "select" | "selectend" | "selectstart" | "submit" | "touchcancel" | "touchend" | "touchmove" | "touchstart" | "unload";
export type PossibleEventTarget = EventTarget & (Element | Document | Window);
export type ListenerObject<T = PossibleEventTarget, H = SupportedEventHandler<T>> = Map<H, AddEventListenerOptions | undefined | boolean>;
export type EventsRegistry<T = PossibleEventTarget> = Record<string, Map<T, ListenerObject<T>>>;
export declare const registry: EventsRegistry;

@@ -17,3 +184,3 @@ /**

*/
export declare const globalListener: (e: Event) => void;
export declare const globalListener: (e: NativeEvent) => void;
/**

@@ -23,3 +190,3 @@ * Register a new listener with its options and attach the `globalListener`

*/
export declare const addListener: (element: EventTarget, eventType: string, listener: EventListener, options?: AddEventListenerOptions) => void;
export declare const addListener: <T extends PossibleEventTarget>(element: T, eventType: NativeEventTypes, listener: SupportedEventHandler<T>, options?: AddEventListenerOptions) => void;
/**

@@ -30,3 +197,3 @@ * Remove a listener from registry and detach the `globalListener`

*/
export declare const removeListener: (element: EventTarget, eventType: string, listener: EventListener, options?: AddEventListenerOptions) => void;
export declare const removeListener: <T extends PossibleEventTarget>(element: T, eventType: NativeEventTypes, listener: SupportedEventHandler<PossibleEventTarget>, options?: AddEventListenerOptions) => void;
export declare const on: typeof addListener;

@@ -33,0 +200,0 @@ export declare const off: typeof removeListener;

51

package.json
{
"name": "@thednp/event-listener",
"author": "thednp",
"version": "2.0.2",
"version": "2.0.3",
"description": "Modern event listener for efficient web applications based on subscribe-publish pattern.",

@@ -39,25 +39,12 @@ "license": "MIT",

},
"scripts": {
"pre-test": "npm run clean-coverage",
"test": "npm run pre-test && cypress run",
"clean-coverage": "rimraf coverage .nyc_output",
"cypress": "npm run pre-test && npx cypress open",
"coverage:report": "nyc report --reporter=lcov --reporter=json --reporter=text --reporter=json-summary",
"format": "prettier --write \"src/**/*.ts\"",
"lint:ts": "eslint -c .eslintrc.cjs --ext .ts src",
"fix:ts": "eslint -c .eslintrc.cjs --ext .ts src --fix",
"dts": "dts-bundle-generator --config ./dts.config.ts",
"build": "npm run lint:ts && vite build && npm run docs && npm run dts",
"docs": "ncp dist/event-listener.js docs/event-listener.js && ncp dist/event-listener.js.map docs/event-listener.js.map"
},
"devDependencies": {
"@bahmutov/cypress-esbuild-preprocessor": "^2.2.0",
"@cypress/code-coverage": "^3.10.8",
"@cypress/code-coverage": "^3.12.0",
"@types/istanbul-lib-instrument": "^1.7.4",
"@typescript-eslint/eslint-plugin": "^5.61.0",
"@typescript-eslint/parser": "^5.61.0",
"cypress": "^12.16.0",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"cypress": "^12.17.4",
"dts-bundle-generator": "^8.0.1",
"eslint": "^8.44.0",
"eslint-plugin-jsdoc": "^46.4.3",
"eslint": "^8.48.0",
"eslint-plugin-jsdoc": "^46.5.1",
"eslint-plugin-prefer-arrow": "^1.2.3",

@@ -71,5 +58,23 @@ "eslint-plugin-prettier": "^4.2.1",

"rimraf": "^5.0.1",
"typescript": "^5.1.6",
"vite": "^4.3.9"
"typescript": "^5.2.2",
"vite": "^4.4.9"
},
"packageManager": "pnpm@8.6.12",
"engines": {
"node": ">=16",
"pnpm": ">=8.6.0"
},
"scripts": {
"pre-test": "pnpm clean-coverage",
"test": "pnpm pre-test && cypress run",
"clean-coverage": "rimraf coverage .nyc_output",
"cypress": "pnpm pre-test && cypress open",
"coverage:report": "nyc report --reporter=lcov --reporter=json --reporter=text --reporter=json-summary",
"format": "prettier --write \"src/**/*.ts\"",
"lint:ts": "eslint -c .eslintrc.cjs --ext .ts src",
"fix:ts": "eslint -c .eslintrc.cjs --ext .ts src --fix",
"dts": "dts-bundle-generator --config ./dts.config.ts",
"build": "pnpm lint:ts && vite build && pnpm dts",
"docs": "ncp dist/event-listener.js docs/event-listener.js && ncp dist/event-listener.js.map docs/event-listener.js.map"
}
}
}

@@ -8,6 +8,6 @@ ## EventListener

[![jsDeliver](https://img.shields.io/jsdelivr/npm/hw/@thednp/event-listener)](https://www.jsdelivr.com/package/npm/@thednp/event-listener)
[![cypress version](https://img.shields.io/badge/cypress-12.16.0-brightgreen)](https://cypress.io/)
[![typescript version](https://img.shields.io/badge/typescript-5.1.6-brightgreen)](https://www.typescriptlang.org/)
[![eslint version](https://img.shields.io/badge/eslint-8.44.0-brightgreen)](https://github.com/eslint)
[![vite version](https://img.shields.io/badge/vite-4.3.9-brightgreen)](https://github.com/vitejs)
[![cypress version](https://img.shields.io/badge/cypress-12.17.4-brightgreen)](https://cypress.io/)
[![typescript version](https://img.shields.io/badge/typescript-5.2.2-brightgreen)](https://www.typescriptlang.org/)
[![eslint version](https://img.shields.io/badge/eslint-8.48.0-brightgreen)](https://github.com/eslint)
[![vite version](https://img.shields.io/badge/vite-4.4.9-brightgreen)](https://github.com/vitejs)
[![prettier version](https://img.shields.io/badge/prettier-2.8.8-brightgreen)](https://prettier.io/)

@@ -19,3 +19,3 @@

- **EventListener** is TypeScript sourced;
- **EventListener** is TypeScript sourced, with some types addapted from React;
- **EventListener** makes use of the native [Map](https://caniuse.com/mdn-javascript_builtins_map) to subscribe/register or unsubscribe/remove listeners, which is perfect since we need to make sure the exact listeners are added/removed; this completely invalidates the need to [deconstruct function objects](https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript) for comparison's sake to make sure event listeners are properly handled;

@@ -51,3 +51,3 @@ - **EventListener** allows you to register multiple listeners for the same target, even of the same type, but always uses a single `globalListener` to call them all at once when event is triggered;

// add a listener with `useCapture: false`
function handleMyClick(e) {
function handleMyClick(e: Listener.NativeEvent) {
if (e.target.tagName === 'button') {

@@ -107,8 +107,13 @@ e.preventDefault();

You can also make use of "tree shaking" to import only the module you want, for instance:
You can also make use of the types for more consistent code:
```js
import { on } from '@thednp/event-listener';
import { on, FocusEventHandler } from '@thednp/event-listener';
on(document, handleMyClick, true);
const handleMyFocus: FocusEventHandler<HTMLInputElement> = (e) => {
console.log(e)
}
const myInput = document.querySelector('input') || document.createElement('input');
on(myInput, 'focus', handleMyFocus);
```

@@ -118,3 +123,3 @@

## Run the tests suite (new)
## Run the tests suite

@@ -121,0 +126,0 @@ - [Download](https://github.com/thednp/event-listener/archive/refs/heads/master.zip) the package from Github;

@@ -9,4 +9,42 @@ /**

type ListenerObject = Map<EventListener, AddEventListenerOptions | undefined | boolean>;
type EventsRegistry = Record<string, Map<EventTarget, ListenerObject>>;
import {
SupportedEventObject,
SupportedEventHandler,
EventHandler,
NativeEventTypes,
NativeEvent,
ClipboardEvent,
CompositionEvent,
DragEvent,
FocusEvent,
FormEvent,
ChangeEvent,
KeyboardEvent,
MouseEvent,
TouchEvent,
PointerEvent,
UIEvent,
WheelEvent,
AnimationEvent,
TransitionEvent,
NativeEventHandler,
ClipboardEventHandler,
CompositionEventHandler,
DragEventHandler,
FocusEventHandler,
FormEventHandler,
ChangeEventHandler,
KeyboardEventHandler,
MouseEventHandler,
TouchEventHandler,
PointerEventHandler,
UIEventHandler,
WheelEventHandler,
AnimationEventHandler,
TransitionEventHandler,
PossibleEventTarget,
ListenerObject,
EventsRegistry,
} from './types';
const registry: EventsRegistry = {};

@@ -20,3 +58,3 @@

*/
const globalListener = (e: Event): void => {
const globalListener = (e: NativeEvent) => {
const { type, currentTarget } = e;

@@ -28,3 +66,3 @@

[...listenersMap].forEach(([listener, options]) => {
listener.apply(element, [e]);
(listener as EventHandler<typeof e>).apply(element, [e]);

@@ -43,6 +81,6 @@ if (typeof options === 'object' && options.once) {

*/
const addListener = (
element: EventTarget,
eventType: string,
listener: EventListener,
const addListener = <T extends PossibleEventTarget>(
element: T,
eventType: NativeEventTypes,
listener: SupportedEventHandler<T>,
options?: AddEventListenerOptions,

@@ -54,3 +92,3 @@ ): void => {

}
const oneEventMap = registry[eventType];
const oneEventMap = registry[eventType] as unknown as Map<T, ListenerObject<T, SupportedEventHandler<T>>>;

@@ -66,7 +104,7 @@ if (!oneEventMap.has(element)) {

// register listener with its options
oneElementMap.set(listener, options);
oneElementMap.set(listener as SupportedEventHandler<PossibleEventTarget>, options);
// add listener last
if (!size) {
element.addEventListener(eventType, globalListener, options);
element.addEventListener(eventType, globalListener as unknown as EventListenerObject, options);
}

@@ -80,6 +118,6 @@ };

*/
const removeListener = (
element: EventTarget,
eventType: string,
listener: EventListener,
const removeListener = <T extends PossibleEventTarget>(
element: T,
eventType: NativeEventTypes,
listener: SupportedEventHandler<PossibleEventTarget>,
options?: AddEventListenerOptions,

@@ -103,3 +141,3 @@ ): void => {

if (!oneElementMap || !oneElementMap.size) {
element.removeEventListener(eventType, globalListener, eventOptions);
element.removeEventListener(eventType, globalListener as unknown as EventListenerObject, eventOptions);
}

@@ -113,1 +151,40 @@ };

export { addListener, removeListener, on, off, globalListener, registry };
export type {
SupportedEventObject,
SupportedEventHandler,
EventHandler,
NativeEventTypes,
NativeEvent,
ClipboardEvent,
CompositionEvent,
DragEvent,
FocusEvent,
FormEvent,
ChangeEvent,
KeyboardEvent,
MouseEvent,
TouchEvent,
PointerEvent,
UIEvent,
WheelEvent,
AnimationEvent,
TransitionEvent,
NativeEventHandler,
ClipboardEventHandler,
CompositionEventHandler,
DragEventHandler,
FocusEventHandler,
FormEventHandler,
ChangeEventHandler,
KeyboardEventHandler,
MouseEventHandler,
TouchEventHandler,
PointerEventHandler,
UIEventHandler,
WheelEventHandler,
AnimationEventHandler,
TransitionEventHandler,
PossibleEventTarget,
ListenerObject,
EventsRegistry,
};

@@ -5,3 +5,3 @@ {

"lib": ["DOM", "ESNext", "DOM.Iterable"],
"types": ["vite", "vite/client", "cypress"],
"types": ["vite", "vite/client"],
"rootDir": "./src",

@@ -8,0 +8,0 @@ "baseUrl": "./",

@@ -27,5 +27,4 @@ import path from 'path';

},
target: 'ESNext',
sourcemap: true,
},
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet