Skip to content

Commit

Permalink
Share some code
Browse files Browse the repository at this point in the history
- reduce some code repetition by moving a few functions in ga4-core
  • Loading branch information
andysellick committed Nov 8, 2022
1 parent 8f42c98 commit 5d485be
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//= require ../vendor/polyfills/closest.js
window.GOVUK = window.GOVUK || {}
window.GOVUK.analyticsGa4 = window.GOVUK.analyticsGa4 || {};

Expand Down Expand Up @@ -47,6 +48,54 @@ window.GOVUK.analyticsGa4 = window.GOVUK.analyticsGa4 || {};

getGemVersion: function () {
return window.GOVUK.analyticsGa4.vars.gem_version || 'not found'
},

trackFunctions: {
findTrackingAttributes: function (clicked, trackingTrigger) {
if (clicked.hasAttribute('[' + trackingTrigger + ']')) {
return clicked
} else {
return clicked.closest('[' + trackingTrigger + ']')
}
},

// create an object to split up long URLs and get around the 100 character limit on GTM data
// this gets reassembled in GA4
populateLinkPathParts: function (href) {
var path = ''
if (this.hrefIsRelative(href) || this.isMailToLink(href)) {
path = href
} else {
// This regex matches a protocol and domain name at the start of a string such as https://www.gov.uk, http://gov.uk, //gov.uk
path = href.replace(/^(http:||https:)?(\/\/)([^\/]*)/, '') // eslint-disable-line no-useless-escape
}

if (path === '/' || path.length === 0) {
return
}

/*
This will create an object with 5 keys that are indexes ("1", "2", etc.)
The values will be each part of the link path split every 100 characters, or undefined.
For example: {"1": "/hello/world/etc...", "2": "/more/path/text...", "3": undefined, "4": undefined, "5": undefined}
Undefined values are needed to override the persistent object in GTM so that any values from old pushes are overwritten.
*/
var parts = path.match(/.{1,100}/g)
var obj = {}
for (var i = 0; i < 5; i++) {
obj[(i + 1).toString()] = parts[i]
}
return obj
},

hrefIsRelative: function (href) {
// Checks that a link is relative, but is not a protocol relative url
return href[0] === '/' && href[1] !== '/'
},

isMailToLink: function (href) {
return href.substring(0, 7) === 'mailto:'
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// = require govuk/vendor/polyfills/Element/prototype/closest.js
//= require ../vendor/polyfills/closest.js
window.GOVUK = window.GOVUK || {}
window.GOVUK.Modules = window.GOVUK.Modules || {};

Expand Down Expand Up @@ -29,7 +29,7 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
}

Ga4EventTracker.prototype.trackClick = function (event) {
var target = this.findTrackingAttributes(event.target)
var target = window.GOVUK.analyticsGa4.core.trackFunctions.findTrackingAttributes(event.target, this.trackingTrigger)
if (target) {
var schema = new window.GOVUK.analyticsGa4.Schemas().eventSchema()

Expand Down Expand Up @@ -88,14 +88,6 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
}
}

Ga4EventTracker.prototype.findTrackingAttributes = function (clicked) {
if (clicked.hasAttribute('[' + this.trackingTrigger + ']')) {
return clicked
} else {
return clicked.closest('[' + this.trackingTrigger + ']')
}
}

// check if an attribute exists or contains the attribute
Ga4EventTracker.prototype.getClosestAttribute = function (clicked, attribute) {
var isAttributeOnElement = clicked.getAttribute(attribute)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
}

Ga4LinkTracker.prototype.trackClick = function (event) {
var target = this.findTrackingAttributes(event.target)
var target = window.GOVUK.analyticsGa4.core.trackFunctions.findTrackingAttributes(event.target, this.trackingTrigger)
if (target) {
var schema = new window.GOVUK.analyticsGa4.Schemas().eventSchema()

Expand All @@ -58,6 +58,7 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
schema.event = 'event_data'
data.text = event.target.textContent
data.url = this.findLink(event.target).getAttribute('href')
data.link_path_parts = window.GOVUK.analyticsGa4.core.trackFunctions.populateLinkPathParts(data.url)
// get attributes from the data attribute to send to GA
// only allow it if it already exists in the schema
for (var property in data) {
Expand All @@ -78,14 +79,5 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
}
}

// FIXME duplicated from the event tracker - move to somewhere shared
Ga4LinkTracker.prototype.findTrackingAttributes = function (clicked) {
if (clicked.hasAttribute('[' + this.trackingTrigger + ']')) {
return clicked
} else {
return clicked.closest('[' + this.trackingTrigger + ']')
}
}

Modules.Ga4LinkTracker = Ga4LinkTracker
})(window.GOVUK.Modules)
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ window.GOVUK.analyticsGa4.analyticsModules = window.GOVUK.analyticsGa4.analytics
if (clickData.index_total) {
clickData.index_total = parseInt(clickData.index_total)
}
} else if (this.isMailToLink(href)) {
} else if (window.GOVUK.analyticsGa4.core.trackFunctions.isMailToLink(href)) {
clickData.event_name = 'navigation'
clickData.type = 'email'
clickData.external = 'true'
Expand All @@ -98,7 +98,7 @@ window.GOVUK.analyticsGa4.analyticsModules = window.GOVUK.analyticsGa4.analytics
if (clickData.url) {
clickData.url = this.removeCrossDomainParams(clickData.url)
clickData.link_domain = this.populateLinkDomain(clickData.url)
clickData.link_path_parts = this.populateLinkPathParts(clickData.url)
clickData.link_path_parts = window.GOVUK.analyticsGa4.core.trackFunctions.populateLinkPathParts(clickData.url)
}

var schema = new window.GOVUK.analyticsGa4.Schemas().eventSchema()
Expand All @@ -116,40 +116,13 @@ window.GOVUK.analyticsGa4.analyticsModules = window.GOVUK.analyticsGa4.analytics
}
},

populateLinkPathParts: function (href) {
var path = ''
if (this.hrefIsRelative(href) || this.isMailToLink(href)) {
path = href
} else {
// This regex matches a protocol and domain name at the start of a string such as https://www.gov.uk, http://gov.uk, //gov.uk
path = href.replace(/^(http:||https:)?(\/\/)([^\/]*)/, '') // eslint-disable-line no-useless-escape
}

if (path === '/' || path.length === 0) {
return
}

/*
This will create an object with 5 keys that are indexes ("1", "2", etc.)
The values will be each part of the link path split every 100 characters, or undefined.
For example: {"1": "/hello/world/etc...", "2": "/more/path/text...", "3": undefined, "4": undefined, "5": undefined}
Undefined values are needed to override the persistent object in GTM so that any values from old pushes are overwritten.
*/
var parts = path.match(/.{1,100}/g)
var obj = {}
for (var i = 0; i < 5; i++) {
obj[(i + 1).toString()] = parts[i]
}
return obj
},

populateLinkDomain: function (href) {
// We always want mailto links to have an undefined link_domain
if (this.isMailToLink(href)) {
if (window.GOVUK.analyticsGa4.core.trackFunctions.isMailToLink(href)) {
return undefined
}

if (this.hrefIsRelative(href)) {
if (window.GOVUK.analyticsGa4.core.trackFunctions.hrefIsRelative(href)) {
return this.getProtocol() + '//' + this.getHostname()
} else {
// This regex matches a protocol and domain name at the start of a string such as https://www.gov.uk, http://gov.uk, //gov.uk
Expand Down Expand Up @@ -203,10 +176,6 @@ window.GOVUK.analyticsGa4.analyticsModules = window.GOVUK.analyticsGa4.analytics
}
},

isMailToLink: function (href) {
return href.substring(0, 7) === 'mailto:'
},

isDownloadLink: function (href) {
if (this.isInternalLink(href) && this.hrefPointsToDownloadPath(href)) {
return true
Expand All @@ -223,7 +192,7 @@ window.GOVUK.analyticsGa4.analyticsModules = window.GOVUK.analyticsGa4.analytics
},

isInternalLink: function (href) {
if (this.hrefIsRelative(href) || this.hrefIsAnchor(href)) {
if (window.GOVUK.analyticsGa4.core.trackFunctions.hrefIsRelative(href) || this.hrefIsAnchor(href)) {
return true
}
var result = false
Expand Down Expand Up @@ -294,11 +263,6 @@ window.GOVUK.analyticsGa4.analyticsModules = window.GOVUK.analyticsGa4.analytics
return string.substring(string.length - stringToFind.length, string.length) === stringToFind
},

hrefIsRelative: function (href) {
// Checks that a link is relative, but is not a protocol relative url
return href[0] === '/' && href[1] !== '/'
},

hrefIsAnchor: function (href) {
return href[0] === '#'
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ describe('GA4 click tracker', function () {
expected.event_data.event_name = 'navigation'
expected.event_data.type = 'a link'
expected.govuk_gem_version = 'aVersion'
expected.event_data.link_path_parts = {
1: undefined,
2: undefined,
3: undefined,
4: undefined,
5: undefined,
}
})

it('does not track anything without a data-ga4 attribute', function () {
Expand All @@ -111,15 +118,17 @@ describe('GA4 click tracker', function () {
element.textContent = linkText
expected.event_data.text = linkText
expected.event_data.url = link
expected.event_data.link_path_parts["1"] = link

initModule(element, true)
expect(window.dataLayer[0]).toEqual(expected)
})

it('tracks all links with data-ga4 attributes within a container', function () {
var longLink = '#link1-that-is-deliberately-really-long-to-test-the-url-being-split-into-parts-is-this-a-hundred-characters-yet'
element = document.createElement('div')
element.innerHTML =
'<a class="first" href="#link1">Link 1</a>' +
'<a class="first" href="' + longLink + '">Link 1</a>' +
'<a href="#link2" class="secondWrapper"><span class="second">Link 2</span></a>' +
'<a href="#link3"><span class="third">Link 3</span></a>' +
'<span class="nothing"></span>'
Expand All @@ -129,12 +138,16 @@ describe('GA4 click tracker', function () {
initModule(element, false)

expected.event_data.text = 'Link 1'
expected.event_data.url = '#link1'
expected.event_data.url = longLink
expected.event_data.link_path_parts["1"] = '#link1-that-is-deliberately-really-long-to-test-the-url-being-split-into-parts-is-this-a-hundred-cha'
expected.event_data.link_path_parts["2"] = 'racters-yet'
element.querySelector('.first').click()
expect(window.dataLayer[0]).toEqual(expected)

expected.event_data.text = 'Link 2'
expected.event_data.url = '#link2'
expected.event_data.link_path_parts["1"] = '#link2'
expected.event_data.link_path_parts["2"] = undefined
element.querySelector('.second').click()
expect(window.dataLayer[1]).toEqual(expected)

Expand All @@ -157,6 +170,13 @@ describe('GA4 click tracker', function () {
expected.event_data.event_name = 'navigation'
expected.event_data.type = 'a link'
expected.govuk_gem_version = 'aVersion'
expected.event_data.link_path_parts = {
1: undefined,
2: undefined,
3: undefined,
4: undefined,
5: undefined,
}
})

it('tracks only links within a container', function () {
Expand All @@ -173,16 +193,19 @@ describe('GA4 click tracker', function () {

expected.event_data.text = 'Link 1'
expected.event_data.url = '#link1'
expected.event_data.link_path_parts["1"] = '#link1'
element.querySelector('.first').click()
expect(window.dataLayer[0]).toEqual(expected)

expected.event_data.text = 'Link 2'
expected.event_data.url = '#link2'
expected.event_data.link_path_parts["1"] = '#link2'
element.querySelector('.second').click()
expect(window.dataLayer[1]).toEqual(expected)

expected.event_data.text = 'Link 3'
expected.event_data.url = '#link3'
expected.event_data.link_path_parts["1"] = '#link3'
element.querySelector('.third').click()
expect(window.dataLayer[2]).toEqual(expected)

Expand All @@ -202,6 +225,13 @@ describe('GA4 click tracker', function () {
expected.event_data.event_name = 'navigation'
expected.event_data.type = 'a link'
expected.govuk_gem_version = 'aVersion'
expected.event_data.link_path_parts = {
1: undefined,
2: undefined,
3: undefined,
4: undefined,
5: undefined,
}
})

it('tracks only links within the given class', function () {
Expand All @@ -221,16 +251,19 @@ describe('GA4 click tracker', function () {

expected.event_data.text = 'Link 1'
expected.event_data.url = '#link1'
expected.event_data.link_path_parts["1"] = '#link1'
element.querySelector('.first').click()
expect(window.dataLayer[0]).toEqual(undefined)

expected.event_data.text = 'Link 2'
expected.event_data.url = '#link2'
expected.event_data.link_path_parts["1"] = '#link2'
element.querySelector('.second').click()
expect(window.dataLayer[0]).toEqual(expected)

expected.event_data.text = 'Link 3'
expected.event_data.url = '#link3'
expected.event_data.link_path_parts["1"] = '#link3'
element.querySelector('.third').click()
expect(window.dataLayer[1]).toEqual(expected)

Expand Down

0 comments on commit 5d485be

Please sign in to comment.