Understanding AJAX Forms and Why They Are Difficult to Track
AJAX forms submit data asynchronously without refreshing the page, making them invisible to traditional page-based tracking methods. To track these submissions effectively, we need a JavaScript listener that detects the completion of any AJAX request and pushes data into GTM's dataLayer.
Creating the Custom JavaScript Listener (Original Code)
To track AJAX events, we will implement a custom JavaScript snippet that listens for AJAX requests and, upon completion, pushes a custom event into the GTM dataLayer.
Create a new "Custom HTML" tag in GTM:
In your GTM container, click Tags → New → Tag Configuration → Custom HTML.
Paste the following original JavaScript snippet into the HTML box:
<script>
(function() {
function pushAjaxEvent(url, status, response) {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'customAjaxSuccess',
ajaxData: {
requestUrl: url,
responseStatus: status,
responseText: response
}
});
}
function interceptAjax() {
if (window.XMLHttpRequest) {
var origOpen = XMLHttpRequest.prototype.open;
var origSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(method, url) {
this._url = url;
return origOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function() {
var xhr = this;
var origOnReadyStateChange = xhr.onreadystatechange;
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
pushAjaxEvent(xhr._url, xhr.status, xhr.responseText);
}
if (origOnReadyStateChange) {
origOnReadyStateChange.apply(xhr, arguments);
}
};
return origSend.apply(xhr, arguments);
};
}
}
// Start intercepting immediately
interceptAjax();
})();
</script>
Create a GTM Trigger Based on the Custom Event
With the listener set up, we now create a GTM trigger to respond to this custom event.
- In GTM, go to Triggers → New → Custom Event.
- Event Name: exactly match the event name pushed by our JavaScript snippet:
customAjaxSuccess - Trigger fires on: All Custom Events.
- Name this trigger clearly:
Trigger - AJAX Form Submit - Click Save.
Setting up GA4 Event Tag in GTM
Next, we configure the GA4 event tag to send these tracked AJAX submissions to Google Analytics.
- Create a new tag: Tags → New → Tag Configuration → Google Analytics: GA4 Event.
- Configuration Tag: Select your GA4 configuration tag.
- Event Name: Choose a clear name, e.g., ajax_form_submission.
- Under Event Parameters, you can add the following for more detailed tracking:
Parameter Name | Value (from Data Layer) |
---|---|
ajax_url | {{DLV - ajaxData.requestUrl}} |
ajax_status | {{DLV - ajaxData.responseStatus}} |
ajax_response | {{DLV - ajaxData.responseText}} (Optional, ensure no sensitive data) |
Creating Data Layer Variables:
To access these parameters, first create Data Layer variables in GTM:
Go to Variables → New → Data Layer Variable.
- For URL:
Name: DLV - ajaxData.requestUrl
Data Layer Variable Name: ajaxData.requestUrl - For Status:
Name: DLV - ajaxData.responseStatus
Data Layer Variable Name: ajaxData.responseStatus - For Response (optional):
Name: DLV - ajaxData.responseText
Data Layer Variable Name: ajaxData.responseText
Save each variable individually.
Finalizing the Tag:
- Triggering: Choose the previously created trigger (Trigger - AJAX Form Submit).
- Name the Tag clearly: GA4 - AJAX Form Submission.
- Click Save.
Preview, Debug, and Test in GTM
Always test before publishing:
- Click Preview in GTM.
- Submit an AJAX form on your website.
- In GTM Preview, confirm:
The custom event (customAjaxSuccess) triggers.
The GA4 event tag (GA4 - AJAX Form Submission) fires properly.
Verifying Data in Google Analytics (GA4)
After successful debugging:
- Go to your GA4 property → Reports → Engagement → Events.
- Check for your event (ajax_form_submission).
(Events may take up to 24 hours to appear, or use GA4’s DebugView for real-time verification.)
Advantages of This Custom Listener Approach:
- Universal solution: Independent of third-party libraries like jQuery.
- Highly scalable: Automatically captures all AJAX requests without needing to alter individual forms.
- Rich detail: Provides useful context such as request URLs and statuses.