delegate-it
Advanced tools
Comparing version 5.0.0 to 6.0.0
@@ -1,14 +0,2 @@ | ||
import type { ParseSelector } from 'typed-query-selector/parser.d.js'; | ||
export declare type DelegateOptions = boolean | Omit<AddEventListenerOptions, 'once'>; | ||
export declare type EventType = keyof GlobalEventHandlersEventMap; | ||
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; | ||
}; | ||
/** | ||
* Delegates event to a selector. | ||
* @param options A boolean value setting options.capture or an options object of type AddEventListenerOptions without the `once` option | ||
*/ | ||
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): void; | ||
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): void; | ||
export default delegate; | ||
export * from './delegate.js'; | ||
export { default as oneEvent } from './one-event.js'; |
82
index.js
@@ -1,80 +0,2 @@ | ||
/** Keeps track of raw listeners added to the base elements to avoid duplication */ | ||
const ledger = new WeakMap(); | ||
function editLedger(wanted, baseElement, callback, setup) { | ||
if (!wanted && !ledger.has(baseElement)) { | ||
return false; | ||
} | ||
const elementMap = ledger.get(baseElement) | ||
?? new WeakMap(); | ||
ledger.set(baseElement, elementMap); | ||
if (!wanted && !ledger.has(baseElement)) { | ||
return false; | ||
} | ||
const setups = elementMap.get(callback) ?? new Set(); | ||
elementMap.set(callback, setups); | ||
const existed = setups.has(setup); | ||
if (wanted) { | ||
setups.add(setup); | ||
} | ||
else { | ||
setups.delete(setup); | ||
} | ||
return existed && wanted; | ||
} | ||
function isEventTarget(elements) { | ||
return typeof elements.addEventListener === 'function'; | ||
} | ||
function safeClosest(event, selector) { | ||
let target = event.target; | ||
if (target instanceof Text) { | ||
target = target.parentElement; | ||
} | ||
if (target instanceof Element && event.currentTarget instanceof Element) { | ||
// `.closest()` may match ancestors of `currentTarget` but we only need its children | ||
const closest = target.closest(selector); | ||
if (closest && event.currentTarget.contains(closest)) { | ||
return closest; | ||
} | ||
} | ||
} | ||
// This type isn't exported as a declaration, so it needs to be duplicated above | ||
function delegate(base, selector, type, callback, options) { | ||
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; | ||
const { signal } = listenerOptions; | ||
if (signal?.aborted) { | ||
return; | ||
} | ||
// Handle Selector-based usage | ||
if (typeof base === 'string') { | ||
base = document.querySelectorAll(base); | ||
} | ||
// Handle Array-like based usage | ||
if (!isEventTarget(base)) { | ||
for (const element of base) { | ||
delegate(element, selector, type, callback, listenerOptions); | ||
} | ||
return; | ||
} | ||
// `document` should never be the base, it's just an easy way to define "global event listeners" | ||
const baseElement = base instanceof Document ? base.documentElement : base; | ||
// Handle the regular Element usage | ||
const capture = Boolean(typeof options === 'object' ? options.capture : options); | ||
const listenerFn = (event) => { | ||
const delegateTarget = safeClosest(event, selector); | ||
if (delegateTarget) { | ||
const delegateEvent = Object.assign(event, { delegateTarget }); | ||
callback.call(baseElement, delegateEvent); | ||
} | ||
}; | ||
const setup = JSON.stringify({ selector, type, capture }); | ||
const isAlreadyListening = editLedger(true, baseElement, callback, setup); | ||
if (!isAlreadyListening) { | ||
baseElement.addEventListener(type, listenerFn, listenerOptions); | ||
} | ||
signal?.addEventListener('abort', () => { | ||
editLedger(false, baseElement, callback, setup); | ||
}); | ||
} | ||
export default delegate; | ||
export * from './delegate.js'; | ||
export { default as oneEvent } from './one-event.js'; |
{ | ||
"name": "delegate-it", | ||
"version": "5.0.0", | ||
"version": "6.0.0", | ||
"description": "Lightweight and modern event delegation in the browser", | ||
@@ -19,10 +19,16 @@ "keywords": [ | ||
"repository": "fregante/delegate-it", | ||
"funding": "https://github.com/sponsors/fregante", | ||
"license": "MIT", | ||
"author": "Federico Brigante <me@fregante.com> (https://fregante.com)", | ||
"type": "module", | ||
"main": "index.js", | ||
"module": "index.js", | ||
"exports": "./index.js", | ||
"main": "./index.js", | ||
"types": "./index.d.ts", | ||
"files": [ | ||
"index.js", | ||
"index.d.ts" | ||
"index.d.ts", | ||
"delegate.js", | ||
"delegate.d.ts", | ||
"one-event.js", | ||
"one-event.d.ts" | ||
], | ||
@@ -32,6 +38,5 @@ "scripts": { | ||
"prepack": "tsc --sourceMap false", | ||
"test": "tsc && xo && ava", | ||
"watch": "run-p --silent watch:*", | ||
"watch:build": "tsc --watch", | ||
"watch:test": "ava --watch" | ||
"test": "tsc && xo && vitest run", | ||
"watch": "tsc --watch", | ||
"watch:test": "vitest" | ||
}, | ||
@@ -48,13 +53,12 @@ "xo": { | ||
"dependencies": { | ||
"typed-query-selector": "^2.6.1" | ||
"typed-query-selector": "^2.10.0" | ||
}, | ||
"devDependencies": { | ||
"@sindresorhus/tsconfig": "^3.0.1", | ||
"ava": "^4.3.0", | ||
"jsdom": "^20.0.0", | ||
"npm-run-all": "^4.1.5", | ||
"sinon": "^14.0.0", | ||
"typescript": "^4.7.4", | ||
"xo": "^0.50.0" | ||
"@types/jsdom": "^21.1.1", | ||
"jsdom": "^21.1.1", | ||
"typescript": "^5.0.4", | ||
"vitest": "^0.30.1", | ||
"xo": "^0.54.1" | ||
} | ||
} |
@@ -8,5 +8,5 @@ # delegate-it [![][badge-gzip]][link-bundlephobia] | ||
This is a fork of the popular [`delegate`](https://github.com/zenorocha/delegate) with some improvements: | ||
This is a fork of the popular but abandoned [`delegate`](https://github.com/zenorocha/delegate) with some improvements: | ||
- modern: ES2021, TypeScript, Edge 16+ (it uses `WeakMap` and `Element.closest()`) | ||
- modern: ES2022, TypeScript, Edge 16+ (it uses `WeakMap` and `Element.closest()`) | ||
- idempotent: identical listeners aren't added multiple times, just like the native `addEventListener` | ||
@@ -33,6 +33,4 @@ - debugged ([2d54c11](https://github.com/fregante/delegate-it/commit/2d54c1182aefd3ec9d8250fda76290971f5d7166), [c6bb88c](https://github.com/fregante/delegate-it/commit/c6bb88c2aa8097b25f22993a237cf09c96bcbfb8)) | ||
#### With an element as base | ||
```js | ||
delegate(document.body, '.btn', 'click', event => { | ||
delegate('.btn', 'click', event => { | ||
console.log(event.delegateTarget); | ||
@@ -42,40 +40,32 @@ }); | ||
#### With a selector (of existing elements) as base | ||
### With listener options | ||
```js | ||
delegate('.container', '.btn', 'click', event => { | ||
delegate('.btn', 'click', event => { | ||
console.log(event.delegateTarget); | ||
}, { | ||
capture: true | ||
}); | ||
``` | ||
#### With an array/array-like of elements as base | ||
### On a custom base | ||
```js | ||
delegate(document.querySelectorAll('.container'), '.btn', 'click', event => { | ||
console.log(event.delegateTarget); | ||
}); | ||
``` | ||
Use this option if you don't want to have a global listener attached on `html`, it improves performance: | ||
#### With listener options | ||
```js | ||
delegate(document.body, '.btn', 'click', event => { | ||
delegate('.btn', 'click', event => { | ||
console.log(event.delegateTarget); | ||
}, true); | ||
// Or equivalent: | ||
delegate(document.body, '.btn', 'click', event => { | ||
console.log(event.delegateTarget); | ||
}, { | ||
capture: true | ||
base: document.querySelector('main') | ||
}); | ||
``` | ||
**Note:** the `once` option is currently not supported. | ||
### Remove event delegation | ||
```js | ||
const controller = delegate(document.body, '.btn', 'click', event => { | ||
const controller = new AbortController(); | ||
delegate('.btn', 'click', event => { | ||
console.log(event.delegateTarget); | ||
}, { | ||
signal: controller.signal, | ||
}); | ||
@@ -86,14 +76,22 @@ | ||
### Listen to one event only | ||
```js | ||
const controller = new AbortController(); | ||
delegate(document.body, '.btn', 'click', event => { | ||
console.log(event.delegateTarget); | ||
delegate('.btn', 'click', event => { | ||
console.log('This will only be called once'); | ||
}, { | ||
signal: controller.signal, | ||
once: true | ||
}); | ||
``` | ||
controller.abort(); | ||
### Listen to one event only, with a promise | ||
```js | ||
import {oneEvent} from 'delegate-it'; | ||
await oneEvent('.btn', 'click'); | ||
console.log('The body was clicked'); | ||
``` | ||
### Custom event types in Typescript | ||
## TypeScript | ||
@@ -100,0 +98,0 @@ If you're using TypeScript and have event types that are custom, you can override the global `GlobalEventHandlersEventMap` interface via declaration merging. e.g. say you have a `types/globals.d.ts` file, you can add the following. |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
11728
6
9
113
124
1
Updatedtyped-query-selector@^2.10.0