diff --git a/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-core.js b/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-core.js
index d7cce95250..00d57512be 100644
--- a/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-core.js
+++ b/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-core.js
@@ -1,3 +1,4 @@
+//= require ../vendor/polyfills/closest.js
window.GOVUK = window.GOVUK || {}
window.GOVUK.analyticsGa4 = window.GOVUK.analyticsGa4 || {};
@@ -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:'
+ }
}
}
diff --git a/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-event-tracker.js b/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-event-tracker.js
index 49f803f2b7..4a32232aa9 100644
--- a/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-event-tracker.js
+++ b/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-event-tracker.js
@@ -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 || {};
@@ -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()
@@ -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)
diff --git a/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.js b/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.js
index 40446421ee..44b1078ef7 100644
--- a/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.js
+++ b/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.js
@@ -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()
@@ -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) {
@@ -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)
diff --git a/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-specialist-link-tracker.js b/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-specialist-link-tracker.js
index e14da762b4..92d14e062f 100644
--- a/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-specialist-link-tracker.js
+++ b/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-specialist-link-tracker.js
@@ -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'
@@ -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()
@@ -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
@@ -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
@@ -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
@@ -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] === '#'
},
diff --git a/spec/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.spec.js b/spec/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.spec.js
index 8d58e61898..e023ec9576 100644
--- a/spec/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.spec.js
+++ b/spec/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.spec.js
@@ -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 () {
@@ -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 =
- 'Link 1' +
+ 'Link 1' +
'Link 2' +
'Link 3' +
''
@@ -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)
@@ -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 () {
@@ -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)
@@ -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 () {
@@ -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)