@@ -38,77 +38,83 @@ export const formInteractionTracking = (): EnrichmentPlugin => {
3838 const name = '@amplitude/plugin-form-interaction-tracking-browser' ;
3939 const type = 'enrichment' ;
4040 const setup = async ( config : BrowserConfig , amplitude : BrowserClient ) => {
41- /* istanbul ignore if */
42- if ( ! amplitude ) {
43- // TODO: Add required minimum version of @amplitude /analytics-browser
44- config . loggerProvider . warn (
45- 'Form interaction tracking requires a later version of @amplitude/analytics-browser. Form interaction events are not tracked.' ,
46- ) ;
47- return ;
48- }
49-
50- /* istanbul ignore if */
51- if ( typeof document === 'undefined' ) {
52- return ;
53- }
54-
55- const addFormInteractionListener = ( form : HTMLFormElement ) => {
56- let hasFormChanged = false ;
57-
58- addEventListener ( form , 'change' , ( ) => {
59- if ( ! hasFormChanged ) {
60- amplitude . track ( DEFAULT_FORM_START_EVENT , {
61- [ FORM_ID ] : stringOrUndefined ( form . id ) ,
62- [ FORM_NAME ] : stringOrUndefined ( form . name ) ,
63- [ FORM_DESTINATION ] : form . action ,
64- } ) ;
65- }
66- hasFormChanged = true ;
67- } ) ;
41+ // The form interaction plugin observes changes in the dom. For this to work correctly, the observer can only be setup
42+ // after the body is built. When Amplitud gets initialized in a script tag, the body tag is still unavailable. So register this
43+ // only after the window is loaded
44+ // eslint-disable-next-line no-restricted-globals
45+ window . addEventListener ( 'load' , function ( ) {
46+ /* istanbul ignore if */
47+ if ( ! amplitude ) {
48+ // TODO: Add required minimum version of @amplitude /analytics-browser
49+ config . loggerProvider . warn (
50+ 'Form interaction tracking requires a later version of @amplitude/analytics-browser. Form interaction events are not tracked.' ,
51+ ) ;
52+ return ;
53+ }
54+
55+ /* istanbul ignore if */
56+ if ( typeof document === 'undefined' ) {
57+ return ;
58+ }
59+
60+ const addFormInteractionListener = ( form : HTMLFormElement ) => {
61+ let hasFormChanged = false ;
62+
63+ addEventListener ( form , 'change' , ( ) => {
64+ if ( ! hasFormChanged ) {
65+ amplitude . track ( DEFAULT_FORM_START_EVENT , {
66+ [ FORM_ID ] : stringOrUndefined ( form . id ) ,
67+ [ FORM_NAME ] : stringOrUndefined ( form . name ) ,
68+ [ FORM_DESTINATION ] : form . action ,
69+ } ) ;
70+ }
71+ hasFormChanged = true ;
72+ } ) ;
6873
69- addEventListener ( form , 'submit' , ( ) => {
70- if ( ! hasFormChanged ) {
71- amplitude . track ( DEFAULT_FORM_START_EVENT , {
74+ addEventListener ( form , 'submit' , ( ) => {
75+ if ( ! hasFormChanged ) {
76+ amplitude . track ( DEFAULT_FORM_START_EVENT , {
77+ [ FORM_ID ] : stringOrUndefined ( form . id ) ,
78+ [ FORM_NAME ] : stringOrUndefined ( form . name ) ,
79+ [ FORM_DESTINATION ] : form . action ,
80+ } ) ;
81+ }
82+
83+ amplitude . track ( DEFAULT_FORM_SUBMIT_EVENT , {
7284 [ FORM_ID ] : stringOrUndefined ( form . id ) ,
7385 [ FORM_NAME ] : stringOrUndefined ( form . name ) ,
7486 [ FORM_DESTINATION ] : form . action ,
7587 } ) ;
76- }
77-
78- amplitude . track ( DEFAULT_FORM_SUBMIT_EVENT , {
79- [ FORM_ID ] : stringOrUndefined ( form . id ) ,
80- [ FORM_NAME ] : stringOrUndefined ( form . name ) ,
81- [ FORM_DESTINATION ] : form . action ,
88+ hasFormChanged = false ;
8289 } ) ;
83- hasFormChanged = false ;
84- } ) ;
85- } ;
86-
87- // Adds listener to existing anchor tags
88- const forms = Array . from ( document . getElementsByTagName ( 'form' ) ) ;
89- forms . forEach ( addFormInteractionListener ) ;
90-
91- // Adds listener to anchor tags added after initial load
92- /* istanbul ignore else */
93- if ( typeof MutationObserver !== 'undefined' ) {
94- observer = new MutationObserver ( ( mutations ) => {
95- mutations . forEach ( ( mutation ) => {
96- mutation . addedNodes . forEach ( ( node ) => {
97- if ( node . nodeName === 'FORM' ) {
98- addFormInteractionListener ( node as HTMLFormElement ) ;
99- }
100- if ( 'querySelectorAll' in node && typeof node . querySelectorAll === 'function' ) {
101- Array . from ( node . querySelectorAll ( 'form' ) as HTMLFormElement [ ] ) . map ( addFormInteractionListener ) ;
102- }
90+ } ;
91+
92+ // Adds listener to existing anchor tags
93+ const forms = Array . from ( document . getElementsByTagName ( 'form' ) ) ;
94+ forms . forEach ( addFormInteractionListener ) ;
95+
96+ // Adds listener to anchor tags added after initial load
97+ /* istanbul ignore else */
98+ if ( typeof MutationObserver !== 'undefined' ) {
99+ observer = new MutationObserver ( ( mutations ) => {
100+ mutations . forEach ( ( mutation ) => {
101+ mutation . addedNodes . forEach ( ( node ) => {
102+ if ( node . nodeName === 'FORM' ) {
103+ addFormInteractionListener ( node as HTMLFormElement ) ;
104+ }
105+ if ( 'querySelectorAll' in node && typeof node . querySelectorAll === 'function' ) {
106+ Array . from ( node . querySelectorAll ( 'form' ) as HTMLFormElement [ ] ) . map ( addFormInteractionListener ) ;
107+ }
108+ } ) ;
103109 } ) ;
104110 } ) ;
105- } ) ;
106111
107- observer . observe ( document . body , {
108- subtree : true ,
109- childList : true ,
110- } ) ;
111- }
112+ observer . observe ( document . body , {
113+ subtree : true ,
114+ childList : true ,
115+ } ) ;
116+ }
117+ } ) ;
112118 } ;
113119 const execute = async ( event : Event ) => event ;
114120 const teardown = async ( ) => {
0 commit comments