@@ -7,9 +7,15 @@ import type { IEventListenerManager } from '../interface/event-listener-manager'
77export class EventListenerManager implements IEventListenerManager {
88 /**
99 * Map that stores the mapping from original listeners to wrapped listeners
10- * Structure: Map<eventType, Map<originalListener, wrappedListener >>
10+ * Structure: Map<eventType, Map<originalListener, Map<capture, wrappedRecord> >>
1111 */
12- protected _listenerMap : Map < string , Map < EventListenerOrEventListenerObject , EventListener > > ;
12+ protected _listenerMap : Map <
13+ string ,
14+ Map <
15+ EventListenerOrEventListenerObject ,
16+ Map < boolean , { wrappedListener : EventListener ; options ?: boolean | AddEventListenerOptions } >
17+ >
18+ > ;
1319
1420 /**
1521 * Transformer function that transforms the event
@@ -44,6 +50,16 @@ export class EventListenerManager implements IEventListenerManager {
4450 return ;
4551 }
4652
53+ const capture = this . _resolveCapture ( options ) ;
54+ const once = this . _resolveOnce ( options ) ;
55+ const listenerTypeMap = this . _getOrCreateListenerTypeMap ( type ) ;
56+ const wrappedMap = this . _getOrCreateWrappedMap ( listenerTypeMap , listener ) ;
57+
58+ // Align with native behavior: adding same (type, listener, capture) repeatedly is a no-op.
59+ if ( wrappedMap . has ( capture ) ) {
60+ return ;
61+ }
62+
4763 // Create a wrapped listener that applies the transformation
4864 const wrappedListener = ( event : Event ) => {
4965 const transformedEvent = this . _eventListenerTransformer ( event ) ;
@@ -52,13 +68,15 @@ export class EventListenerManager implements IEventListenerManager {
5268 } else if ( listener . handleEvent ) {
5369 listener . handleEvent ( transformedEvent ) ;
5470 }
71+
72+ // Native once listeners are removed automatically after dispatch, clear the mapping as well.
73+ if ( once ) {
74+ this . _deleteListenerRecord ( type , listener , capture ) ;
75+ }
5576 } ;
5677
5778 // Store the mapping between original and wrapped listener
58- if ( ! this . _listenerMap . has ( type ) ) {
59- this . _listenerMap . set ( type , new Map ( ) ) ;
60- }
61- this . _listenerMap . get ( type ) ! . set ( listener , wrappedListener ) ;
79+ wrappedMap . set ( capture , { wrappedListener, options } ) ;
6280
6381 // Add the wrapped listener
6482 this . _nativeAddEventListener ( type , wrappedListener , options ) ;
@@ -79,17 +97,12 @@ export class EventListenerManager implements IEventListenerManager {
7997 return ;
8098 }
8199
82- // Get the wrapped listener from our map
83- const wrappedListener = this . _listenerMap . get ( type ) ?. get ( listener ) ;
84- if ( wrappedListener ) {
100+ const capture = this . _resolveCapture ( options ) ;
101+ const wrappedRecord = this . _listenerMap . get ( type ) ?. get ( listener ) ?. get ( capture ) ;
102+ if ( wrappedRecord ) {
85103 // Remove the wrapped listener
86- this . _nativeRemoveEventListener ( type , wrappedListener , options ) ;
87-
88- // Remove from our map
89- this . _listenerMap . get ( type ) ! . delete ( listener ) ;
90- if ( this . _listenerMap . get ( type ) ! . size === 0 ) {
91- this . _listenerMap . delete ( type ) ;
92- }
104+ this . _nativeRemoveEventListener ( type , wrappedRecord . wrappedListener , capture ) ;
105+ this . _deleteListenerRecord ( type , listener , capture ) ;
93106 }
94107 }
95108
@@ -105,14 +118,74 @@ export class EventListenerManager implements IEventListenerManager {
105118 * Clear all event listeners
106119 */
107120 clearAllEventListeners ( ) : void {
108- this . _listenerMap . forEach ( ( listenersMap , type ) => {
109- listenersMap . forEach ( ( wrappedListener , originalListener ) => {
110- this . _nativeRemoveEventListener ( type , wrappedListener , undefined ) ;
121+ this . _listenerMap . forEach ( ( listenerMap , type ) => {
122+ listenerMap . forEach ( wrappedMap => {
123+ wrappedMap . forEach ( ( wrappedRecord , capture ) => {
124+ this . _nativeRemoveEventListener ( type , wrappedRecord . wrappedListener , capture ) ;
125+ } ) ;
111126 } ) ;
112127 } ) ;
113128 this . _listenerMap . clear ( ) ;
114129 }
115130
131+ protected _resolveCapture ( options ?: boolean | EventListenerOptions | AddEventListenerOptions ) : boolean {
132+ if ( typeof options === 'boolean' ) {
133+ return options ;
134+ }
135+ return ! ! options ?. capture ;
136+ }
137+
138+ protected _resolveOnce ( options ?: boolean | AddEventListenerOptions ) : boolean {
139+ return typeof options === 'object' && ! ! options ?. once ;
140+ }
141+
142+ protected _getOrCreateListenerTypeMap (
143+ type : string
144+ ) : Map <
145+ EventListenerOrEventListenerObject ,
146+ Map < boolean , { wrappedListener : EventListener ; options ?: boolean | AddEventListenerOptions } >
147+ > {
148+ let listenerTypeMap = this . _listenerMap . get ( type ) ;
149+ if ( ! listenerTypeMap ) {
150+ listenerTypeMap = new Map ( ) ;
151+ this . _listenerMap . set ( type , listenerTypeMap ) ;
152+ }
153+ return listenerTypeMap ;
154+ }
155+
156+ protected _getOrCreateWrappedMap (
157+ listenerTypeMap : Map <
158+ EventListenerOrEventListenerObject ,
159+ Map < boolean , { wrappedListener : EventListener ; options ?: boolean | AddEventListenerOptions } >
160+ > ,
161+ listener : EventListenerOrEventListenerObject
162+ ) : Map < boolean , { wrappedListener : EventListener ; options ?: boolean | AddEventListenerOptions } > {
163+ let wrappedMap = listenerTypeMap . get ( listener ) ;
164+ if ( ! wrappedMap ) {
165+ wrappedMap = new Map ( ) ;
166+ listenerTypeMap . set ( listener , wrappedMap ) ;
167+ }
168+ return wrappedMap ;
169+ }
170+
171+ protected _deleteListenerRecord ( type : string , listener : EventListenerOrEventListenerObject , capture : boolean ) : void {
172+ const listenerTypeMap = this . _listenerMap . get ( type ) ;
173+ if ( ! listenerTypeMap ) {
174+ return ;
175+ }
176+ const wrappedMap = listenerTypeMap . get ( listener ) ;
177+ if ( ! wrappedMap ) {
178+ return ;
179+ }
180+ wrappedMap . delete ( capture ) ;
181+ if ( wrappedMap . size === 0 ) {
182+ listenerTypeMap . delete ( listener ) ;
183+ }
184+ if ( listenerTypeMap . size === 0 ) {
185+ this . _listenerMap . delete ( type ) ;
186+ }
187+ }
188+
116189 /**
117190 * Native implementation of addEventListener
118191 * To be implemented by derived classes
0 commit comments