Skip to main content

Form tracking

Snowplow form tracking creates three event types: change_form, submit_form and focus_form. Using the enableFormTracking method adds event listeners to the document listening for events from form elements and their interactive fields (that is, all inputtextarea, and select elements).

note

Events on password fields will not be tracked.

Form events are automatically tracked once configured.

Installation

Tracker DistributionIncluded
sp.js
sp.lite.js

Download:

Download from GitHub Releases (Recommended)Github Releases (plugins.umd.zip)
Available on jsDelivrjsDelivr (latest)
Available on unpkgunpkg (latest)

Note: The links to the CDNs above point to the current latest version. You should pin to a specific version when integrating this plugin on your website if you are using a third party CDN in production.

Enable form tracking

window.snowplow('addPlugin',
"https://cdn.jsdelivr.net/npm/@snowplow/browser-plugin-form-tracking@latest/dist/index.umd.min.js",
["snowplowFormTracking", "FormTrackingPlugin"]
);

snowplow('enableFormTracking');

By default, all three event types are tracked. However, it is possible to subscribe only to specific event types using the options.events option when enabling form tracking:

// subscribing to specific event types
snowplow('enableFormTracking', {
options: {
events: ['submit_form', 'focus_form', 'change_form']
},
});

change_form

When a user changes the value of a textareainput, or select element inside a form, a change_form event will be fired. It will capture the name, type, and new value of the element, and the id of the parent form.

submit_form

When a user submits a form, a submit_form event will be fired. It will capture the id and classes of the form and the name, type, and value of all textareainput, and select elements inside the form.

Note that this will only work if the original form submission event is actually fired. If you prevent it from firing, for example by using a jQuery event handler which returns false to handle clicks on the form's submission button, the Snowplow submit_form event will not be fired.

focus_form

When a user focuses on a form element, a focus_form event will be fired. It will capture the id and classes of the form and the name, type, and value of the textareainput, or select element inside the form that received focus.

Configuration

It may be that you do not want to track every field in a form, or every form on a page. You can customize form tracking by passing a configuration argument to the enableFormTracking method. This argument should be an object with two elements named "forms" and "fields". The "forms" element determines which forms will be tracked; the "fields" element determines which fields inside the tracked forms will be tracked. As with link click tracking, there are three ways to configure each field: a denylist, an allowlist, or a filter function. You do not have to use the same method for both fields.

Denylists

This is an array of strings used to prevent certain elements from being tracked. Any form with a CSS class in the array will be ignored. Any field whose name property is in the array will be ignored. All other elements will be tracked.

Allowlists

This is an array of strings used to turn on tracking. Any form with a CSS class in the array will be tracked. Any field in a tracked form whose name property is in the array will be tracked. All other elements will be ignored.

Filter functions

This is a function used to determine which elements are tracked. The element is passed as the argument to the function and is tracked if and only if the value returned by the function is truthy.

Transform functions

This is a function used to transform data in each form field. The value and element are passed as arguments to the function and the tracked value is replaced by the value returned.

The transform function receives three arguments:

  1. The value of the element.
  2. Either the HTML element (for change_form and focus_form events) or an instance of ElementData (for submit_form events).
  3. The HTML element (in all form tracking events).

The function signature is:

type transformFn = (
elementValue: string | null,
elementInfo: ElementData | TrackedHTMLElement,
elt: TrackedHTMLElement
) => string | null;

This means that you can specify a transform function that applies the exact same logic to all submit_form, change_form and focus_form events independent of the element's attributes the logic may depend on. For example:

function redactPII(eltValue, _, elt) {
if (elt.id === 'pid') {
return 'redacted';
}
return eltValue;
}

snowplow('enableFormTracking', {
options: {
fields: { transform: redactPII },
},
});

Examples

To track every form element and every field except those fields named "password":

var opts = {
forms: {
denylist: []
},
fields: {
denylist: ['password']
}
};

snowplow('enableFormTracking', { options: opts });

To track only the forms with CSS class "tracked", and only those fields whose ID is not "private":

var opts = {
forms: {
allowlist: ["tracked"]
},
fields: {
filter: function (elt) {
return elt.id !== "private";
}
}
};

snowplow('enableFormTracking', { options: opts });

To transform the form fields with an MD5 hashing function:

function hashMD5(value, _, elt) {
// can use elt to make transformation decisions
return MD5(value);
}

var opts = {
forms: {
allowlist: ["tracked"]
},
fields: {
filter: function (elt) {
return elt.id !== "private";
},
transform: hashMD5
}
};

snowplow('enableFormTracking', { options: opts });

Tracking forms embedded inside iframes

The options for tracking forms inside of iframes are limited – browsers block access to contents of iframes that are from different domains than the parent page. We are not able to provide a solution to track events using trackers initialized on the parent page in such cases.

It is possible to track events from forms embedded in iframes loaded from the same domain as the parent page or iframes created using JavaScript on the parent page (e.g. HubSpot forms).

In case you are able to access form elements inside an iframe, you can pass them in the options.forms argument when calling enableFormTracking on the parent page. This will enable form tracking for the specific form elements. The feature may also be used for forms not embedded in iframes, but it's most useful in this particular case.

The following example shows how to identify the form elements inside an iframe and pass them to the enableFormTracking function:

let iframe = document.getElementById('form_iframe'); // find the element for the iframe
let forms = iframe.contentWindow.document.getElementsByTagName('form'); // find form elements inside the iframe
snowplow('enableFormTracking', {
options: {
forms: forms // pass the embedded forms when enabling form tracking
},
});

Alternatively, you can specify the iframe's document as a target directly; this will enable form tracking for all forms within the iframe's document:

let iframe = document.getElementById('form_iframe'); // find the element for the iframe
let formDoc = iframe.contentWindow.document; // find iframe document that contains forms
snowplow('enableFormTracking', {
options: {
targets: [document, formDoc] // pass the embedded document when enabling form tracking
},
});

targets can also be used to only track subsets of a document by passing a parent element directly.

Tracking forms from inside shadow trees

Forms created within shadow trees (e.g. within custom Web Components) can only be tracked once the user first focuses a field.

The plugin relies on composed events to detect the form interactions at the document level. Only focus events are considered composed, change and submit events are not composed and so are not automatically detected by the plugin.

When the user focuses a field in a form that is detected as being inside a shadow tree, the event listeners are added directly to the form element within the shadow tree in addition to the document-level event listeners in order to track future change and submit events correctly. If the form has no interactive field elements to first trigger the focus event, any change or submit events that fire will not be tracked.

If the shadow root is attached in "closed" mode, no events will be tracked for elements in that shadow tree, only "open" mode is supported.

Custom context entities

Context entities can be sent with all form tracking events by supplying them in an array in the context argument.

snowplow('enableFormTracking', { options: {}, context: [] });

These context entities can be dynamic, i.e. they can be traditional self-describing JSON objects, or callbacks that generate valid self-describing JSON objects.

For form change events, context generators are passed (elt, type, value), and form submission events are passed (elt, innerElements).

A dynamic context could therefore look something like this for form change events:

let dynamicContext = function (elt, type, value) {
// perform operations here to construct the context entity
return context;
};

snowplow('enableFormTracking', { options: {}, context: [dynamicContext] });