“ GTM is blocked on Opera as seen on SimoAhava site ”
// this code should be put inside documentReady callback to ensure all scripts are loaded
if ('undefined' == typeof dataLayer || 'function' != typeof dataLayer['push']) {
// GTM is blocked, do something about it...
}
The simplest thing you can do when GTM is blocked is logging an event to GTM. Unfortunately, there is currently no way (that I know of) to send an event to GTM once it is blocked, but we can send the event directly to GA. It's a series of unfortunate events that when GTM is blocked GA is also blocked. Lucky for us, however, Google does allow us to interact with GA server side, so there is always light behind the clouds after all.
We can check to see if GA is loaded by checking the existence of the ga object:
// this code should be put inside documentReady callback to ensure all scripts are loaded
if ('undefined' == typeof ga || 'function' != typeof ga['send']) {
// GA is blocked, do something about it...
}
Once we know that GA is blocked, we can simply ask our server to send the tracking event to GA instead. There are many ways to do that, you can certainly send an Ajax request to the server to do that. In my case, I prefer to serve a fake 1-pixel image like this:
var img = document.createElement('img');
img.setAttribute('style', 'display:none;');
// to prevent the browser from serving the cached image, we can append the current timestamp to the image src
var src = '/ut.php?ga={{ ga }}&action=pageview&image=1&block=1&ts=' + Date.now();
if (document.referrer) {
src += '&document_referrer=' + encodeURIComponent(document.referrer);
}
img.src = src;
document.body.appendChild(img);
The Javascript code above will append an image to your current website that will look like this:
<img src="/ut.php?ts=123131321321"/>
The idea is really straightforward since GA is not loaded on the browser we cannot use it to send the event to GA server, we will ask our server do it for us instead.
Before we move to the next step: in my example code, I will use PHP but you can use whatever you want (ASP or anything you want). For the file extension, I use .php for convenience but if I wanted to I could have even used something like ut.jpeg.
Below is a simplified version of ut.php
// print the blank image
header('Content-Type: image/png');
echo base64_decode('R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs');
// assuming you use composer, we need to include the autoload file
require 'vendor/autoload.php';
// we use this GA library to make things easier for us
// <a href="https://github.com/theiconic/php-ga-measurement-protocol">https://github.com/theiconic/php-ga-measurement-pr...</a>
use TheIconic\Tracking\GoogleAnalytics\Analytics;
$analytics = new Analytics();
$cid = null;
if (isset($_COOKIE['_ga'])) {
$gaCookie = explode('.', $_COOKIE['_ga']);
if (isset($gaCookie[2])) {
// check if uuid
if (preg_match(
'#^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$#i',
$gaCookie[2]
)) {
// uuid set in cookie
$cid = $gaCookie[2];
} elseif (isset($gaCookie[2]) && isset($gaCookie[3])) {
// google old client id
$cid = $gaCookie[2] . '.' . $gaCookie[3];
}
}
}
// if we do not have the customer id yet, we can generate a new one
if (!$cid) {
$cid = sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
// 32 bits for "time_low"
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
// 16 bits for "time_mid"
mt_rand(0, 0xffff),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 4
mt_rand(0, 0x0fff) | 0x4000,
// 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
mt_rand(0, 0x3fff) | 0x8000,
// 48 bits for "node"
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0xffff)
);
// here we set back this cookie for the user. We set to _ga right now but perhaps it's better to set to another custom key?
// see <a href="https://stackoverflow.com/questions/16102436/what-are-the-values-in-ga-cookie">https://stackoverflow.com/questions/16102436/what-...</a> for more information
setcookie("_ga", sprintf('%s.%s.%s.%s', 1, '/', $cid , time()), time() + 63113904); /* expire in 2 years */
}
$analytics->setProtocolVersion('1')
->setTrackingId($context['ga'])
->setClientId($cid);
// for pageView event, we may want to send the document path
// we can guess this from the HTTP_REFERER
// another solution is to pass this information directly via your query string
$parsed = parse_url($_SERVER['HTTP_REFERER']);
$path = $parsed['path'];
if (!empty($parsed['query'])) {
$path .= '?' . $parsed['query'];
}
$analytics->setDocumentPath($path);
// we may also want to pass the document referrer of the original pageView
// you can use javascript to get this information and pass via query string
if (isset($_GET['document_referrer'])) {
$analytics->setDocumentReferrer($_GET['document_referrer']);
}
$analytics->setIpOverride(getClientIp());
$analytics->sendPageview();
// helper function to get client ip
// note that this information is not 100% reliable
function getClientIp()
{
return getenv('HTTP_CLIENT_IP') ?:
getenv('HTTP_X_FORWARDED_FOR') ?:
getenv('HTTP_X_FORWARDED') ?:
getenv('HTTP_FORWARDED_FOR') ?:
getenv('HTTP_FORWARDED') ?:
getenv('REMOTE_ADDR');
}
[cta3 variant="3"]
// set the category, action, and label to what you like, you can later use these values to filter and export reports
$analytics->setEventCategory('block')
->setEventAction('block')
->setEventLabel('block')
->setEventValue(0)
->sendEvent();
Now that we have found a way to log page view event, why not take a step further? Veteran GTM users love it for the ability to easily listen to triggers and perform various things with it. Unfortunately, you cannot do any of these cool things if GTM is blocked, but perhaps you can still track important events and log them to your favorite Analytics tool such as GA?
Normally, GTM users will add event tracking code directly on GTM which is extremely convenient, but it also means that once GTM is blocked you will not be able to track ANY event. One option to get around this issue is to write a custom wrapper event listener and dispatcher that will still work when GTM and/or GA is blocked. Below is a sample piece of code to dispatch event event:
if ('undefined' != typeof dataLayer && 'function' == typeof dataLayer['push']) {
// send with GTM
// dataLayer requires the event key
if (!data.hasOwnProperty('event') && data.hasOwnProperty('eventLabel')) {
data['event'] = data['eventLabel'];
}
dataLayer.push(data);
} else if ('undefined' != typeof ga && 'function' == typeof ga['send']) {
// fallback to JS GA
if (!data.hasOwnProperty('hitType')) {
data['hitType'] = 'event';
}
ga('send', data);
} else {
// fallback to server side GA
// use our custom GA
var xhr;
if (window.XMLHttpRequest) {
// code for IE7+, Firefox, Chrome, Opera, Safari
xhr = new XMLHttpRequest();
} else {
// code for IE6, IE5
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
// use Server Side Tracking
// there will be limitation such as GTM does not have support for this
// we simply try to collect the information available to us
xhr.open('GET', '/ut.php?action=event&context=' + encodeURIComponent(JSON.stringify(this.context)) + '&push=' + encodeURIComponent(JSON.stringify(data)) + '&ts=' + Date.now());
xhr.send();
}
As you can see, we check if GTM is available, if yes then we dispatch event through GTM, if not we fallback to GA and then later to our server-side code. In my example, I encode the event data in a json conext string to pass to the server, so in the server code I will have to parse it then send the event (in my case, I send it to GA) like this:
if (isset($_GET['push'])) {
$push = json_decode($_GET['push'], true);
if (is_array($push)) {
if (isset($push['eventCategory'])) {
$ec = $push['eventCategory'];
}
if (isset($push['eventAction'])) {
$ea = $push['eventAction'];
}
if (isset($push['eventLabel'])) {
$el = $push['eventLabel'];
}
if (isset($push['eventValue'])) {
$ev = $push['eventValue'];
}
}
}
// get event category
if (isset($_GET['event_category'])) {
$ec = $_GET['event_category'];
}
if (isset($_GET['event_action'])) {
$ea = $_GET['event_action'];
}
if (isset($_GET['event_label'])) {
$el = $_GET['event_label'];
}
if (isset($_GET['event_value'])) {
$ev = $_GET['event_value'];
}
$analytics->setEventCategory($ec)
->setEventAction($ea)
->setEventLabel($el)
->setEventValue($ev)
->sendEvent();
Obviously, you don't have to use GA, you can use Yandex Metrica or any other Analytics tool. Simply customize your server code to handle the event data the way you want.
Do not rely on Google Tag Manager for mission critical tasks. Expect GTM to be blocked and all the tags that it's supposed to serve will never run. I recommend you to keep the important code outside of GTM, be nice to your developers or find a system that allows you to edit the code directly without asking your developers.
Big HINT: Do you know that Nilead uses our own Nilead Platform for all our website projects, and it does allow you to edit everything including CSS, HTML, Javascript on every page on your website without having to beg the developers to do so you? For the marketers who really really hate dealing with code, you can also utilize our Website Manage Service to have us do all these codes edits for you promptly and efficiently.
The source code of all the above code plus some BONUS will be packed and posted here soon. Subscribe to our newsletter or leave a comment (see below), or subscribe to one of our social media to get notified once they are available in the next few days.
© Copyright © 2021 by Nilead. All rights reserved