@@ -40,6 +40,8 @@ const defaultFormTrackingEvents = [
4040
4141/** Form tracking plugin options to determine which events to fire and the elements to listen for */
4242interface FormTrackingOptions {
43+ /** Whether to handle events in the capture phase or the bubbling phase. Capture is usually more reliable, but may trigger early if you need changes from other submit handlers in your transforms, filters, or context generators. Defaults to true. */
44+ useCapture ?: boolean ;
4345 /** List of `form` elements that are allowed to generate events, or criteria for deciding that when the event listener handles the event */
4446 forms ?:
4547 | FilterCriterion < HTMLElement >
@@ -86,6 +88,7 @@ const _focusListeners: Record<string, EventListener> = {};
8688const _changeListeners : Record < string , EventListener > = { } ;
8789const _submitListeners : Record < string , EventListener > = { } ;
8890const _targets : Record < string , EventTarget [ ] > = { } ;
91+ const _captures : Record < string , boolean > = { } ;
8992
9093/**
9194 * Add submission/focus/change event listeners to page for forms and elements according to `configuration`
@@ -99,19 +102,21 @@ export function addFormListeners(tracker: BrowserTracker, configuration: FormTra
99102
100103 const events = options ?. events ?? defaultFormTrackingEvents ;
101104
105+ const useCapture = ( _captures [ tracker . id ] = options ?. useCapture ?? true ) ;
106+
102107 const targets = ( _targets [ tracker . id ] = getTargetList ( options ?. targets , config . forms ) ) ;
103108
104109 if ( events . indexOf ( FormTrackingEvent . FOCUS_FORM ) !== - 1 ) {
105110 _focusListeners [ tracker . id ] = getFormChangeListener ( tracker , config , FormTrackingEvent . FOCUS_FORM , context ) ;
106- targets . forEach ( ( target ) => addEventListener ( target , 'focus' , _focusListeners [ tracker . id ] , true ) ) ;
111+ targets . forEach ( ( target ) => addEventListener ( target , 'focus' , _focusListeners [ tracker . id ] , true ) ) ; // focus does not bubble
107112 }
108113 if ( events . indexOf ( FormTrackingEvent . CHANGE_FORM ) !== - 1 ) {
109114 _changeListeners [ tracker . id ] = getFormChangeListener ( tracker , config , FormTrackingEvent . CHANGE_FORM , context ) ;
110- targets . forEach ( ( target ) => addEventListener ( target , 'change' , _changeListeners [ tracker . id ] , true ) ) ;
115+ targets . forEach ( ( target ) => addEventListener ( target , 'change' , _changeListeners [ tracker . id ] , useCapture ) ) ;
111116 }
112117 if ( events . indexOf ( FormTrackingEvent . SUBMIT_FORM ) !== - 1 ) {
113118 _submitListeners [ tracker . id ] = getFormSubmissionListener ( tracker , config , context ) ;
114- targets . forEach ( ( target ) => addEventListener ( target , 'submit' , _submitListeners [ tracker . id ] , true ) ) ;
119+ targets . forEach ( ( target ) => addEventListener ( target , 'submit' , _submitListeners [ tracker . id ] , useCapture ) ) ;
115120 }
116121}
117122
@@ -145,10 +150,11 @@ function getTargetList(configTargets: EventTarget[] | undefined, forms: FormConf
145150 */
146151export function removeFormListeners ( tracker : BrowserTracker ) {
147152 const targets = _targets [ tracker . id ] ?? [ document ] ;
153+ const useCapture = _captures [ tracker . id ] ?? true ;
148154 targets . forEach ( ( target ) => {
149- if ( _focusListeners [ tracker . id ] ) target . removeEventListener ( 'focus' , _focusListeners [ tracker . id ] , true ) ;
150- if ( _changeListeners [ tracker . id ] ) target . removeEventListener ( 'change' , _changeListeners [ tracker . id ] , true ) ;
151- if ( _submitListeners [ tracker . id ] ) target . removeEventListener ( 'submit' , _submitListeners [ tracker . id ] , true ) ;
155+ if ( _focusListeners [ tracker . id ] ) target . removeEventListener ( 'focus' , _focusListeners [ tracker . id ] , true ) ; // focus does not bubble
156+ if ( _changeListeners [ tracker . id ] ) target . removeEventListener ( 'change' , _changeListeners [ tracker . id ] , useCapture ) ;
157+ if ( _submitListeners [ tracker . id ] ) target . removeEventListener ( 'submit' , _submitListeners [ tracker . id ] , useCapture ) ;
152158 } ) ;
153159}
154160
@@ -363,10 +369,13 @@ function getFormChangeListener(
363369 // bind late to the forms/field directly on field focus in this case
364370 if ( target !== e . target && e . composed && isTrackableElement ( target ) ) {
365371 if ( target . form ) {
366- if ( _changeListeners [ tracker . id ] ) addEventListener ( target . form , 'change' , _changeListeners [ tracker . id ] , true ) ;
367- if ( _submitListeners [ tracker . id ] ) addEventListener ( target . form , 'submit' , _submitListeners [ tracker . id ] , true ) ;
372+ if ( _changeListeners [ tracker . id ] )
373+ addEventListener ( target . form , 'change' , _changeListeners [ tracker . id ] , _captures [ tracker . id ] ) ;
374+ if ( _submitListeners [ tracker . id ] )
375+ addEventListener ( target . form , 'submit' , _submitListeners [ tracker . id ] , _captures [ tracker . id ] ) ;
368376 } else {
369- if ( _changeListeners [ tracker . id ] ) addEventListener ( target , 'change' , _changeListeners [ tracker . id ] , true ) ;
377+ if ( _changeListeners [ tracker . id ] )
378+ addEventListener ( target , 'change' , _changeListeners [ tracker . id ] , _captures [ tracker . id ] ) ;
370379 }
371380 }
372381
0 commit comments