Track outbound links with Google Analytics (gtag.js)
This script will track outgoing links in Google Analytics using the gtag.js method (ie: global site tag). This should not be confused with Google Tag Manager. This is based on my previous script for Google Universal Analytics.js, but with some necessary changes to account for the newer gtag.js.
There is an official tutorial that also shows you how to do this, but that method is very flawed as it requires you to manually update all outbound link code on your website, doesn’t account for target
properties, and will break if Google Analytics is blocked (ad blocking). The solution I wrote below integrates seamlessly, requires no change to your own HTML code (other than to include the JavaScript), and doesn’t break if Google Analytics is blocked or fails to load.
Automatically track outbound links
The best method of “auto-tracking” outgoing links is to automatically detect outbound links with JavaScript when they are clicked, and automatically track it as an event. The method below uses a “callback” function that, once the click has been registered with Analytics, the link is opened. This prevents the situation whereby the browser starts opening the new page before Analytics has had a chance to register the click. This method is by far the most robust, and simply means you need to include an external JavaScript file on your pages.
Features
- Tracks all outgoing links as events in Google Analytics, including outbound link & referring page
- Uses callbacks to first track and then redirect when outbound link opens in current window
- Detects and works with Ctrl/Shift/Meta & middle-mouse clicks
- Works even if Google Analytics is blocked
Tracking script
<script>
function _gaLt(event) {
// if GA is blocked or not loaded, or not main|middle|touch click then don't track
if (typeof ga == 'undefined' || (event.which != 1 && event.which != 2)) {
return;
}
var el = event.srcElement || event.target;
// loop up the DOM tree through parent elements if clicked element is not a link (eg: an image inside a link)
while (el && (typeof el.tagName == 'undefined' || el.tagName.toLowerCase() != 'a' || !el.href)) {
el = el.parentNode;
}
// if a link with valid href has been clicked
if (el && el.href) {
var link = el.href;
// only if it is an external link
if (link.indexOf(location.host) == -1 && !link.match(/^javascript\:/i)) {
// is target set and _(self|parent|top)
var target = (el.target && !el.target.match(/^_(self|parent|top)$/i)) ? el.target : false;
// assume a target if (ctrl|shift|meta)-click
if (event.ctrlKey || event.shiftKey || event.metaKey || event.which == 2) {
target = "_blank";
}
var hbrun = false; // tracker has not yet run
// hitBack to open link in same window after tracker
var hitBack = function () {
// run once only
if (hbrun) return;
hbrun = true;
window.location.href = link;
};
if (target) {
// if target opens a new window then just track
gtag('event', link, {
'event_category': 'Outgoing Links',
'event_label': document.location.pathname + document.location.search
});
} else {
// Prevent standard click, track then open
if (event.type == 'mousedown') {
var blockClick = function (event) {
event.preventDefault();
// remove click event after click
el.removeEventListener('click', boundClick);
}
// bind the click event
var boundClick = blockClick.bind(event)
// prevent the click
el.addEventListener('click', boundClick);
}
event.preventDefault ? event.preventDefault() : event.returnValue = !1;
// send event with callback
gtag('event', link, {
'event_category': 'Outgoing Links',
'event_label': document.location.pathname + document.location.search,
'event_callback': hitBack
});
// Run hitBack again if GA takes longer than 1 second
setTimeout(hitBack, 1000);
}
}
}
}
var _w = window;
/* Use "click" if touchscreen device, else "mousedown" */
var _gaLtEvt = ("ontouchstart" in _w) ? "click" : "mousedown";
/* Attach the event to all clicks in the document after page has loaded */
_w.addEventListener("load", function () { document.body.addEventListener(_gaLtEvt, _gaLt, !1) }, !1);
</script>