While Google Tag Manager provides many customization options in its Google Analytics tag template, it does not, by default, allow for the insertion of instructions between the create
and send
methods of analytics.js. Not having direct access to the ga command queue can sometimes lead to challenges when trying to access the Google Analytics tracker and model objects before a tag is sent.
Solution
The customTask
is a no-operation function that executes first in the sequence of analytics.js tasks. It is designed to be overridden by developers to insert custom instructions that are executed when a tag is sent. In Google Tag Manager it is possible to override the customTask by setting the field on the tag template (Fields to Set) to a Custom JavaScript Variable that returns a function.
Usage Examples
Collecting the Client ID in a custom dimension
Inserting the Client ID value from the tracker object into a custom dimension is a common requirement for users of Google Analytics. It enables user-level reporting outside of the User Explorer report and facilitates online-to-offline integrations by exporting an un-hashed Client ID value to BigQuery. On the surface, this task may involve some simple JavaScript to extract and assign the clientId field value to a custom dimension. However, it is not possible to send the custom dimension with this value for the first pageview of a new user, which leads to an extra hit being required. To side-step this extra hit, set the customTask
field in your Universal Analytics tag to a Custom JavaScript Variable with the function below:
function () {
return function () {
try {
// Retrieve all trackers
var trackers = ga.getAll();
trackers.forEach(function(tracker) {
// Get the Client ID
var cid = tracker.get('clientId');
// Set the Client ID in a custom dimension at index 1
tracker.set('dimension1', cid);
});
} catch (e) {}
}
}
Adding to subsequent tasks
Because customTask is executed first in the tasks sequence it provides an opportunity to override other subsequent tasks. You may replace tasks with your own custom functions or augment the existing functionality by chaining your custom functions to execute before or after an existing task. In the example below, we retrieve the hit payload and send it to a custom endpoint while still retaining the original sendHitTask functionality:
function () {
return function () {
try {
// Get the default GTM tracker by name
var tracker = ga.getByName('gtm1');
// Grab a reference to the default sendHitTask function
var originalSendHitTask = tracker.get('sendHitTask');
// Override the sendHitTask
tracker.set('sendHitTask', function(model) {
// Execute the original sendHitTask
originalSendHitTask(model);
// Send the hit payload to a Cloud Function for processing
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://us-central1-starlit-granite-545.cloudfunctions.net/ga-proxy-collect', true);
xhr.send(model.get('hitPayload'));
});
} catch (e) {}
}
}
Manipulating the hit payload
Having access to the hit payload means it can also be manipulated (with caution). Customizations to the GA tag are configured in the tag template but some corner cases require manual alterations to the payload. An example of this is serving the same mobile web pages for both browsers and webviews in native apps. Most of the app tracking being native it’s sometimes desirable to send screenview type hits from a web container in order to populate a Google Analytics app view. This is possible by configuring the appropriate mobile app fields in the tag template and replacing the hit type:
function () {
return function () {
try {
// Get the default GTM tracker by name
var tracker = ga.getByName('gtm1');
// Grab a reference to the default sendHitTask function
var originalSendHitTask = tracker.get('sendHitTask');
// Override the sendHitTask
tracker.set('sendHitTask', function(model) {
// Replace pageview with screenview in the payload
var newHitPayload = model.get('hitPayload').replace(/t=pageview/, 't=screenview');
// Set the new payload value
model.set('hitPayload', newHitPayload, true);
// Execute the original sendHitTask
originalSendHitTask(model);
});
} catch (e) {}
}
}
Aborting a hit
Similarly to exceptions in triggers and other validation-related tasks, you can add custom filters to the customTask
. An example would be overriding the displayFeaturesTask to null if the user didn't provide consent. Another example could be enforcing a Do Not Track initiative on the client-side by aborting the hit:
function () {
return function () {
// Abort hit when DoNotTrack is configured in the browser
if (window.navigator && 1 == window.navigator.doNotTrack) {
throw "abort";
}
}
}