1010
1111type Handler = ( event : Event ) => void
1212
13- const handlersByType = new Map < string , Set < Handler > > ( )
14-
15- function getOrCreateSet ( eventType : string ) : Set < Handler > {
16- let set = handlersByType . get ( eventType )
17- if ( ! set ) {
18- set = new Set ( )
19- handlersByType . set ( eventType , set )
20- document . addEventListener ( eventType , dispatch )
21- }
22- return set
13+ type DelegatedListener = {
14+ handlers : Set < Handler >
15+ dispatch : ( event : Event ) => void
16+ eventType : string
17+ capture : boolean
2318}
2419
25- function dispatch ( event : Event ) : void {
26- const handlers = handlersByType . get ( event . type )
27- if ( handlers ) {
28- // Safe to iterate directly — mutations (add/remove) only happen in
29- // setup/cleanup, not during dispatch. Set iteration is stable for
30- // entries that existed when iteration began.
31- handlers . forEach ( ( handler ) => {
32- handler ( event )
33- } )
20+ const handlersByType = new Map < string , DelegatedListener > ( )
21+
22+ function getListenerKey ( eventType : string , capture : boolean ) : string {
23+ return `${ eventType } :${ capture ? 'capture' : 'bubble' } `
24+ }
25+
26+ function getOrCreateListener ( eventType : string , capture : boolean ) : DelegatedListener {
27+ const key = getListenerKey ( eventType , capture )
28+ let listener = handlersByType . get ( key )
29+ if ( ! listener ) {
30+ const handlers = new Set < Handler > ( )
31+ const dispatch = ( event : Event ) : void => {
32+ handlers . forEach ( ( handler ) => {
33+ handler ( event )
34+ } )
35+ }
36+ listener = { handlers, dispatch, eventType, capture }
37+ handlersByType . set ( key , listener )
38+ document . addEventListener ( eventType , dispatch , { capture } )
3439 }
40+ return listener
3541}
3642
3743/**
3844 * Register a handler for a document-level event type.
3945 * Returns an unsubscribe function.
4046 */
41- export function addDelegatedEventListener ( eventType : string , handler : Handler ) : ( ) => void {
42- const set = getOrCreateSet ( eventType )
43- set . add ( handler )
47+ export function addDelegatedEventListener (
48+ eventType : string ,
49+ handler : Handler ,
50+ options : AddEventListenerOptions = { } ,
51+ ) : ( ) => void {
52+ const capture = Boolean ( options . capture )
53+ const key = getListenerKey ( eventType , capture )
54+ const listener = getOrCreateListener ( eventType , capture )
55+ listener . handlers . add ( handler )
4456
4557 return ( ) => {
46- set . delete ( handler )
47- if ( set . size === 0 ) {
48- handlersByType . delete ( eventType )
49- document . removeEventListener ( eventType , dispatch )
58+ listener . handlers . delete ( handler )
59+ if ( listener . handlers . size === 0 ) {
60+ handlersByType . delete ( key )
61+ document . removeEventListener ( eventType , listener . dispatch , { capture } )
5062 }
5163 }
5264}
@@ -55,8 +67,10 @@ export function addDelegatedEventListener(eventType: string, handler: Handler):
5567 * Reset for testing purposes.
5668 */
5769export function resetEventDelegation ( ) : void {
58- handlersByType . forEach ( ( _handlers , eventType ) => {
59- document . removeEventListener ( eventType , dispatch )
70+ handlersByType . forEach ( ( listener ) => {
71+ document . removeEventListener ( listener . eventType , listener . dispatch , {
72+ capture : listener . capture ,
73+ } )
6074 } )
6175 handlersByType . clear ( )
6276}
0 commit comments