delegate-it
Advanced tools
Comparing version 3.0.1 to 4.0.0
import type { ParseSelector } from 'typed-query-selector/parser'; | ||
export declare type DelegateOptions = boolean | Omit<AddEventListenerOptions, 'once'>; | ||
export declare type EventType = keyof GlobalEventHandlersEventMap; | ||
declare type GlobalEvent = Event; | ||
declare namespace delegate { | ||
type Subscription = { | ||
destroy: VoidFunction; | ||
}; | ||
type EventHandler<TEvent extends GlobalEvent = GlobalEvent, TElement extends Element = Element> = (event: Event<TEvent, TElement>) => void; | ||
type Event<TEvent extends GlobalEvent = GlobalEvent, TElement extends Element = Element> = TEvent & { | ||
delegateTarget: TElement; | ||
}; | ||
} | ||
export declare type DelegateEventHandler<TEvent extends Event = Event, TElement extends Element = Element> = (event: DelegateEvent<TEvent, TElement>) => void; | ||
export declare type DelegateEvent<TEvent extends Event = Event, TElement extends Element = Element> = TEvent & { | ||
delegateTarget: TElement; | ||
}; | ||
/** | ||
@@ -18,4 +12,4 @@ * Delegates event to a selector. | ||
*/ | ||
declare function delegate<Selector extends string, TElement extends Element = ParseSelector<Selector, HTMLElement>, TEventType extends EventType = EventType>(base: EventTarget | Document | ArrayLike<Element> | string, selector: Selector, type: TEventType, callback: delegate.EventHandler<GlobalEventHandlersEventMap[TEventType], TElement>, options?: DelegateOptions): delegate.Subscription; | ||
declare function delegate<TElement extends Element = HTMLElement, TEventType extends EventType = EventType>(base: EventTarget | Document | ArrayLike<Element> | string, selector: string, type: TEventType, callback: delegate.EventHandler<GlobalEventHandlersEventMap[TEventType], TElement>, options?: DelegateOptions): delegate.Subscription; | ||
declare function delegate<Selector extends string, TElement extends Element = ParseSelector<Selector, HTMLElement>, TEventType extends EventType = EventType>(base: EventTarget | Document | Iterable<Element> | string, selector: Selector, type: TEventType, callback: DelegateEventHandler<GlobalEventHandlersEventMap[TEventType], TElement>, options?: DelegateOptions): AbortController; | ||
declare function delegate<TElement extends Element = HTMLElement, TEventType extends EventType = EventType>(base: EventTarget | Document | Iterable<Element> | string, selector: string, type: TEventType, callback: DelegateEventHandler<GlobalEventHandlersEventMap[TEventType], TElement>, options?: DelegateOptions): AbortController; | ||
export default delegate; |
55
index.js
/** Keeps track of raw listeners added to the base elements to avoid duplication */ | ||
const ledger = new WeakMap(); | ||
function editLedger(wanted, baseElement, callback, setup) { | ||
var _a, _b; | ||
if (!wanted && !ledger.has(baseElement)) { | ||
return false; | ||
} | ||
const elementMap = (_a = ledger.get(baseElement)) !== null && _a !== void 0 ? _a : new WeakMap(); | ||
const elementMap = ledger.get(baseElement) | ||
?? new WeakMap(); | ||
ledger.set(baseElement, elementMap); | ||
@@ -13,3 +13,3 @@ if (!wanted && !ledger.has(baseElement)) { | ||
} | ||
const setups = (_b = elementMap.get(callback)) !== null && _b !== void 0 ? _b : new Set(); | ||
const setups = elementMap.get(callback) ?? new Set(); | ||
elementMap.set(callback, setups); | ||
@@ -43,2 +43,18 @@ const existed = setups.has(setup); | ||
function delegate(base, selector, type, callback, options) { | ||
const internalController = new AbortController(); | ||
const listenerOptions = typeof options === 'object' ? options : { capture: options }; | ||
// Drop unsupported `once` option https://github.com/fregante/delegate-it/pull/28#discussion_r863467939 | ||
delete listenerOptions.once; | ||
if (listenerOptions.signal) { | ||
if (listenerOptions.signal.aborted) { | ||
internalController.abort(); | ||
return internalController; | ||
} | ||
listenerOptions.signal.addEventListener('abort', () => { | ||
internalController.abort(); | ||
}); | ||
} | ||
else { | ||
listenerOptions.signal = internalController.signal; | ||
} | ||
// Handle Selector-based usage | ||
@@ -50,10 +66,6 @@ if (typeof base === 'string') { | ||
if (!isEventTarget(base)) { | ||
const subscriptions = Array.prototype.map.call(base, (element) => delegate(element, selector, type, callback, options)); | ||
return { | ||
destroy() { | ||
for (const subscription of subscriptions) { | ||
subscription.destroy(); | ||
} | ||
}, | ||
}; | ||
for (const element of base) { | ||
delegate(element, selector, type, callback, listenerOptions); | ||
} | ||
return internalController; | ||
} | ||
@@ -67,23 +79,16 @@ // `document` should never be the base, it's just an easy way to define "global event listeners" | ||
if (delegateTarget) { | ||
event.delegateTarget = delegateTarget; | ||
callback.call(baseElement, event); | ||
const delegateEvent = Object.assign(event, { delegateTarget }); | ||
callback.call(baseElement, delegateEvent); | ||
} | ||
}; | ||
// Drop unsupported `once` option https://github.com/fregante/delegate-it/pull/28#discussion_r863467939 | ||
if (typeof options === 'object') { | ||
delete options.once; | ||
} | ||
const setup = JSON.stringify({ selector, type, capture }); | ||
const isAlreadyListening = editLedger(true, baseElement, callback, setup); | ||
const delegateSubscription = { | ||
destroy() { | ||
baseElement.removeEventListener(type, listenerFn, options); | ||
editLedger(false, baseElement, callback, setup); | ||
}, | ||
}; | ||
if (!isAlreadyListening) { | ||
baseElement.addEventListener(type, listenerFn, options); | ||
baseElement.addEventListener(type, listenerFn, listenerOptions); | ||
} | ||
return delegateSubscription; | ||
internalController.signal.addEventListener('abort', () => { | ||
editLedger(false, baseElement, callback, setup); | ||
}); | ||
return internalController; | ||
} | ||
export default delegate; |
{ | ||
"name": "delegate-it", | ||
"version": "3.0.1", | ||
"version": "4.0.0", | ||
"description": "Lightweight and modern event delegation in the browser", | ||
@@ -42,3 +42,2 @@ "keywords": [ | ||
"max-params": "off", | ||
"@typescript-eslint/no-namespace": "off", | ||
"@typescript-eslint/naming-convention": "off" | ||
@@ -52,9 +51,9 @@ } | ||
"@sindresorhus/tsconfig": "^2.0.0", | ||
"ava": "^4.2.0", | ||
"jsdom": "^19.0.0", | ||
"ava": "^4.3.0", | ||
"jsdom": "^20.0.0", | ||
"npm-run-all": "^4.1.5", | ||
"sinon": "^14.0.0", | ||
"typescript": "^4.6.4", | ||
"xo": "^0.48.0" | ||
"typescript": "^4.7.4", | ||
"xo": "^0.50.0" | ||
} | ||
} |
@@ -10,5 +10,6 @@ # delegate-it [![][badge-gzip]][link-bundlephobia] | ||
- modern: ES6, TypeScript, Edge 15+ (it uses `WeakMap` and `Element.closest()`) | ||
- modern: ES2021, TypeScript, Edge 16+ (it uses `WeakMap` and `Element.closest()`) | ||
- idempotent: identical listeners aren't added multiple times, just like the native `addEventListener` | ||
- debugged ([2d54c11](https://github.com/fregante/delegate-it/commit/2d54c1182aefd3ec9d8250fda76290971f5d7166), [c6bb88c](https://github.com/fregante/delegate-it/commit/c6bb88c2aa8097b25f22993a237cf09c96bcbfb8)) | ||
- supports [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) | ||
@@ -76,9 +77,20 @@ If you need IE support, you can keep using [`delegate`](https://github.com/zenorocha/delegate) | ||
```js | ||
const delegation = delegate(document.body, '.btn', 'click', event => { | ||
const controller = delegate(document.body, '.btn', 'click', event => { | ||
console.log(event.delegateTarget); | ||
}); | ||
delegation.destroy(); | ||
controller.abort(); | ||
``` | ||
```js | ||
const controller = new AbortController(); | ||
delegate(document.body, '.btn', 'click', event => { | ||
console.log(event.delegateTarget); | ||
}, { | ||
signal: controller.signal, | ||
}); | ||
controller.abort(); | ||
``` | ||
### Custom event types in Typescript | ||
@@ -85,0 +97,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
11221
126
106