How to track users with web scripts

How to track users with web scripts

This article describes how to write a JavaScript script to send user data back to the server.

I have made a code repository that contains all the following examples, which can be run to see the effect.

Synchronous AJAX

The common practice of sending data back to the server is to put the collected user data in the unloadevent and send it back to the server with an AJAX request.

However, asynchronous AJAX unloadmay not be successful in the event, because the web page is already unloading, and the browser may or may not send it. So, change to synchronous AJAX requests.


window.addEventListener('unload', function (event) {
  let xhr = new XMLHttpRequest();
  xhr.open('post', '/log', false);
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  xhr.send('foo=bar');
});


In the above code, xhr.open()the third parameter of the method is false, indicating a synchronous request.

The biggest problem with this approach is that browsers will gradually disallow using synchronous AJAX on the main thread. So, the above code doesn’t actually work.

Asynchronous AJAX

Asynchronous AJAX actually works. The premise is unloadthat there must be some time-consuming synchronization operations in the event. This allows enough time for the asynchronous AJAX to be sent successfully.


function log() {
  let xhr = new XMLHttpRequest();
  xhr.open('post', '/log', true);
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  xhr.send('foo=bar');
}

window.addEventListener('unload', function(event) {
  log();

  // a time-consuming operation
  for (let i = 1; i < 10000; i++) {
    for (let m = 1; m < 10000; m++) { continue; }
  }
});


In the above code, a double loop is enforced, which prolongs the unloadexecution time of the event, resulting in the successful sending of asynchronous AJAX.

Track user clicks

setTimeoutIt can also delay page unloading to ensure that asynchronous requests are sent successfully. Below is an example that tracks user clicks.


// <a id="target" href="https://www.google.com">click</a>
const clickTime = 350;
const theLink = document.getElementById('target');

function log() {
  let xhr = new XMLHttpRequest();
  xhr.open('post', '/log', true);
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  xhr.send('foo=bar');
}

theLink.addEventListener('click', function (event) {
  event.preventDefault();
  log();

  setTimeout(function () {
    window.location.href = theLink.getAttribute('href');
  }, clickTime);
});


The above code uses a setTimeoutdelay of 350 milliseconds to make the page jump, so that asynchronous AJAX has time to issue.

bounce tracking

To track user clicks, you can also use bounce tracking.

The so-called “bounce tracking” means that when a web page jumps, it jumps to one or more intermediate URLs in order to collect information, and then jumps to the original target URL.


// <a id="target" href="https://www.google.com">click</a>
const theLink = document.getElementById('target');

theLink.addEventListener('click', function (event) {
  event.preventDefault();
  window.location.href = '/jump?url=' + 
    encodeURIComponent(theLink.getAttribute('href'));
});


In the above code, when the user clicks, it will be forced to jump to an intermediate URL, carry the information to the past, and then jump to the original target URL after processing.

Google and Baidu are doing this now. When clicking on the search result, it will bounce several times before jumping to the target URL.

Beacon API

The above practices will delay the unloading of web pages and seriously affect the user experience.

In order to solve the problem that the asynchronous request cannot be successful when the webpage is unloaded, the browser specially implements a Beacon API , which allows the asynchronous request to be separated from the current main thread and sent into the browser process, which can ensure that it can be sent.


window.addEventListener('unload', function (event) {
  navigator.sendBeacon('/log', 'foo=bar');
});


In the above code, the navigator.sendBeacon()method can guarantee that the asynchronous request will be issued. The first parameter is the requested URL and the second parameter is the data to send.

Note that the Beacon API makes a POST request.

ping property

The HTML <a>tag has an pingattribute. As long as the user clicks, a POST request will be issued to the URL specified by the attribute.


<a href="https://baidu.com" ping="/log?foo=bar">
  click
</a>


In the above code, when the user clicks to jump, /loga POST request will be sent to this URL.

pingAttributes cannot specify a data body and seem to only carry information through the URL’s query string.

Reference link