# delegate-it

> Lightweight and modern event delegation in the browser

Latest version **6.4.0** (published 2026-05-01) · MIT license · 0 weekly downloads

## Install

```sh
npm install delegate-it
pnpm add delegate-it
yarn add delegate-it
bun add delegate-it
```

## Health

**Score 60/100 (C)** — status: active.

Positive: has types; esm support; no vulnerabilities; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 6.4.0 |
| Published | 2026-05-01 |
| First published | 2019-03-07 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 1 |
| Unpacked size | 16.6 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 202 |
| Author | Federico Brigante |
| Maintainers | fregante |
| Keywords | delegate, browser, dom, live, selector, delegation, chrome, electron, firefox, safari, event |

## Links

- npm: https://www.npmjs.com/package/delegate-it
- Repository: https://github.com/fregante/delegate-it
- Homepage: https://github.com/fregante/delegate-it#readme
- Issues: https://github.com/fregante/delegate-it/issues
- Funding: https://github.com/sponsors/fregante
- npm.io page: https://npm.io/package/delegate-it

## Dependencies (1)

- [typed-query-selector](https://npm.io/package/typed-query-selector.md) ^2.11.2

## Alternatives

- [babylon](https://npm.io/package/babylon.md) — 5.1M weekly downloads
- [csscolorparser](https://npm.io/package/csscolorparser.md) — 3.7M weekly downloads
- [expr-eval-fork](https://npm.io/package/expr-eval-fork.md) — 1.5M weekly downloads
- [@leeoniya/ufuzzy](https://npm.io/package/@leeoniya/ufuzzy.md) — 247.7K weekly downloads
- [xml-parser](https://npm.io/package/xml-parser.md) — 78.4K weekly downloads

## Recent versions

- 6.4.0 (latest) — 2026-05-01
- 3.0.0-1 (next) — 2021-03-17
- 6.3.0 — 2025-10-11
- 6.2.1 — 2024-10-28
- 6.2.0 — 2024-08-20
- 6.1.0 — 2024-03-18
- 6.0.1 — 2023-04-25
- 6.0.0 — 2023-04-25
- 5.0.0 — 2022-08-31
- 4.0.1 — 2022-06-26
- 4.0.0 — 2022-06-23
- 3.0.1 — 2022-05-13
- 3.0.0 — 2021-04-10
- 3.0.0-0 — 2021-03-17
- 2.0.2 — 2020-11-09
- … 17 more at https://npm.io/package/delegate-it/versions

## README

# delegate-it [![][badge-gzip]][link-bundlephobia]

[badge-gzip]: https://img.shields.io/bundlephobia/minzip/delegate-it.svg?label=gzipped
[link-bundlephobia]: https://bundlephobia.com/result?p=delegate-it

> Lightweight event delegation

This is a fork of the popular but abandoned [`delegate`](https://github.com/zenorocha/delegate) with some improvements:

- modern: ES2022, 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)

## Install

```
npm install delegate-it
```

```js
// This module is only offered as a ES Module
import delegate from 'delegate-it';
```

## Usage

### Add event delegation

```js
delegate('.btn', 'click', event => {
	console.log(event.delegateTarget); // The element matching '.btn' that was clicked
});
```

### Multiple selectors or event types

```js
// Listen to multiple selectors
delegate(['.btn', '.link'], 'click', event => {
	console.log(event.delegateTarget);
});

// Listen to multiple event types
delegate('.btn', ['click', 'keypress'], event => {
	console.log(event.delegateTarget);
});
```

### With listener options

```js
delegate('.btn', 'click', event => {
	console.log(event.delegateTarget);
}, {
	capture: true
});
```

### On a custom base

Use this option if you don't want to have a global listener attached on `html`, it improves performance:

```js
delegate('.btn', 'click', event => {
	console.log(event.delegateTarget);
}, {
	base: document.querySelector('main')
});
```

### Remove event delegation

```js
const controller = new AbortController();
delegate('.btn', 'click', event => {
	console.log(event.delegateTarget);
}, {
	signal: controller.signal,
});

controller.abort();
```

### Listen to one event only

```js
delegate('.btn', 'click', event => {
	console.log('This will only be called once');
}, {
	once: true
});
```

### Listen to one event only, with a promise

```js
import {oneEvent} from 'delegate-it';

const event = await oneEvent('.btn', 'click');
console.log(event.delegateTarget); // The element matching '.btn' that was clicked
```

### Wait for a specific event with a filter

```js
import {oneEvent} from 'delegate-it';

// Resolves only when a .btn with data-id="42" is clicked
const event = await oneEvent('.btn', 'click', {
	filter: event => event.delegateTarget.dataset.id === '42',
});
```

## API

### `delegate(selector, type, callback, options?)`

Attaches a delegated event listener. The actual listener is added to the `base` element (defaults to `document.documentElement`) and the `callback` is only called when the event's target matches `selector`.

Unlike raw `addEventListener`, identical listeners (same `selector`, `type`, `callback`, and `capture` value) are not added multiple times.

#### `selector`

Type: `string | string[]`

A CSS selector string or array of CSS selector strings to match against. The `callback` is called when the event target (or one of its ancestors) matches the selector and is a descendant of `base`.

#### `type`

Type: `string | string[]`

The event type (e.g. `'click'`) or array of event types to listen for.

#### `callback`

Type: `(event: DelegateEvent) => void`

The function to call when the event is triggered. Receives a [`DelegateEvent`](#delegateevent) — a standard `Event` with an added `delegateTarget` property.

#### `options`

Type: `DelegateOptions`

Optional object extending [`AddEventListenerOptions`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options) with one extra field:

| Option | Type | Description |
|---|---|---|
| `base` | `EventTarget` | The element to attach the listener to. Defaults to `document.documentElement`. Use a specific element for better performance. |
| `capture` | `boolean` | Whether to use capture phase. Default: `false`. |
| `once` | `boolean` | If `true`, the listener is removed after its first invocation. |
| `signal` | `AbortSignal` | If provided, the listener is removed when the signal is aborted. |

---

### `oneEvent(selector, type, options?)`

Returns a `Promise` that resolves with the first matching `DelegateEvent`. Useful as an alternative to `delegate` with `{once: true}`.

If the signal is already aborted when `oneEvent` is called, or is aborted before the event fires, the promise resolves with `undefined`.

```js
import {oneEvent} from 'delegate-it';

const event = await oneEvent('.btn', 'click');
// event is a DelegateEvent, or undefined if the signal was aborted
```

#### `selector`

Type: `string | string[]`

A CSS selector string or array of CSS selector strings.

#### `type`

Type: `string`

The event type to listen for.

#### `options`

Type: `OneEventOptions`

Same as `delegate` options, minus `once` (which is always set automatically), plus:

| Option | Type | Description |
|---|---|---|
| `base` | `EventTarget` | The element to attach the listener to. Defaults to `document.documentElement`. |
| `capture` | `boolean` | Whether to use capture phase. Default: `false`. |
| `signal` | `AbortSignal` | If provided, the promise resolves with `undefined` when the signal is aborted. |
| `filter` | `(event: DelegateEvent) => boolean` | If provided, the promise only resolves when this function returns `true`. Events that don't pass the filter are ignored and the listener stays active. |

---

### `DelegateEvent`

A regular DOM [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) extended with one additional property:

#### `delegateTarget`

Type: `Element`

The element that matched the selector. This is different from `event.target`, which is the innermost element that was actually interacted with (e.g. a `<span>` inside a `<button>`), while `delegateTarget` is always the element matching the `selector` (e.g. the `<button>` itself).

```js
delegate('.btn', 'click', event => {
	event.target;         // e.g. <span> inside the button
	event.delegateTarget; // always the <button> matching '.btn'
});
```

## TypeScript

The type of `event.delegateTarget` is inferred from `selector` when possible, using [`typed-query-selector`](https://github.com/nicolo-ribaudo/typed-query-selector). For example, `delegate('button', 'click', ...)` will type `event.delegateTarget` as `HTMLButtonElement` automatically.

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.

```ts
interface GlobalEventHandlersEventMap {
	'details:toggle': UIEvent;
}
```

In the file that imports `EventType`, you will now be able to set the event type to `'details:toggle'`.

```ts
import type {EventType} from 'delegate-it';

const someEventType1: EventType = 'details:toggle'; // all good
const someEventType2: EventType = 'click'; // all good
const someEventType3: EventType = 'some-invalid-event-type'; // no good
```

## Related

- [select-dom](https://github.com/fregante/select-dom) - Lightweight `querySelector`/`All` wrapper that outputs an Array.
- [doma](https://github.com/fregante/doma) - Parse an HTML string into `DocumentFragment` or one `Element`, in a few bytes.
- [Refined GitHub](https://github.com/sindresorhus/refined-github) - Uses this module.

---
_Source: https://npm.io/package/delegate-it · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
