AJAX Listener for GTM

Updated: Monday, October 2, 2023

Why robust?

This AJAX listener is written in pure Javascript and acts as a middleman between your forms and the JavaScript APIs XMLHttpRequest and Fetch. This means that you don’t need to install any external library to make it work.

It listens to any AJAX request that your browser sends (not only requests from forms) so I recommend you to install this listener only on the pages you want to track AJAX requests from.

When an AJAX request is made, it sends the dataLayer event ajax_complete only when a successful response is received from the server. This allows you to track only valid form submissions.

How to make it work?

First, you need to Copy/Paste the following code into a custom HTML tag in Google Tag Manager.

<script>
  window.dataLayer = window.dataLayer || [];
  var origin_open = XMLHttpRequest.prototype.open;
  var origin_send = XMLHttpRequest.prototype.send;
  var xhrRequestBody = "";

  XMLHttpRequest.prototype.send = function(body) {
    if(body instanceof FormData) {
      xhrRequestBody = Object.fromEntries(body);
    } else if(typeof body == "string" || body instanceof String) {
      body = decodeURIComponent(body);
      var a = body.split("&");
      var b = [];
      a.forEach(function(el) { return b.push(el.split("=")) });
      xhrRequestBody = Object.fromEntries(b);
    }
    origin_send.apply(this, arguments);
  };

  XMLHttpRequest.prototype.open = function() {
      this.addEventListener('loadend', function() {
          dataLayer.push({
            event: "ajax_complete",
            requestType: "XHR",
            requestBody: xhrRequestBody || "",
            location: document.location.href || "",
            path: document.location.pathname || "",
            fragment: document.location.hash || "",
            protocol: document.location.protocol || "",
            hostname: document.location.hostname || "",
            statusCode: this.status || "",
            statusText: this.statusText || "",
            responseType: this.responseType || "",
            readyState: this.readyState || ""
          });
        });
      origin_open.apply(this, arguments);
  };

  window.fetch = new Proxy(window.fetch, {
    apply: function (target, that, args) {
      var temp = target.apply(that, args);
      temp.then(function(res) {
        dataLayer.push({
              event: "ajax_complete",
              requestType: "Fetch",
              location: document.location.href || "",
              path: document.location.pathname || "",
              fragment: document.location.hash || "",
              protocol: document.location.protocol || "",
              hostname: document.location.hostname || "",
              statusCode: res.status || "",
              statusText: res.statusText || "",
              requestedURL: res.url || ""
          });
      });
      return temp;
    },
  });
  /*
  * v0.1.0
  * Created by Data Marketing School at http://www.data-marketing-school.com
  * Written by https://www.linkedin.com/in/lucasrollin/
  */
</script>

Then, set up the trigger to make this tag fire only on the pages you need it to.

On every AJAX request, you should see the ajax_complete event in the dataLayer.

What if it doesn’t work for you?

Even though this listener is robust in its conception, it may not work in your website environment.

If it’s the case, feel free to post a comment below this blog post and give all the details regarding your situation. I’ll be happy to help.

Didn't find what your were looking for?

Get help from the Data Marketing Club

Join the club