Skip to content

Commit 480323f

Browse files
authored
Merge pull request #297 from sliderule-analytics/fix-analyticsjs-debug-mode-for-gtag/sc-3915
fix: fixes a sneaky bug that causes all ga4 events (gtag) to fire in debug mode
2 parents 052bd92 + 1da35cb commit 480323f

File tree

1 file changed

+101
-86
lines changed
  • packages/analytics-plugin-google-analytics/src

1 file changed

+101
-86
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* global, window */
2-
let loadedInstances = {}
2+
let loadedInstances = {};
33
/* Location of gtag script */
4-
const gtagScriptSource = 'https://www.googletagmanager.com/gtag/js'
4+
const gtagScriptSource = "https://www.googletagmanager.com/gtag/js";
55
// See https://developers.google.com/analytics/devguides/collection/ga4/reference/config
66
const defaultGtagConf = {
77
// https://support.google.com/analytics/answer/7201382?hl=en#zippy=%2Cglobal-site-tag-websites
@@ -35,15 +35,15 @@ const defaultGtagConf = {
3535
* Cookie Flags
3636
* https://developers.google.com/analytics/devguides/collection/ga4/cookies-user-id#cookie_flags
3737
*/
38-
cookie_flags: ''
39-
}
38+
cookie_flags: "",
39+
};
4040

4141
const defaultConfig = {
42-
gtagName: 'gtag',
43-
dataLayerName: 'ga4DataLayer',
42+
gtagName: "gtag",
43+
dataLayerName: "ga4DataLayer",
4444
measurementIds: [],
4545
gtagConfig: defaultGtagConf,
46-
}
46+
};
4747

4848
/**
4949
* Google analytics plugin
@@ -70,83 +70,93 @@ const defaultConfig = {
7070
* })
7171
*/
7272
function googleAnalytics(pluginConfig = {}) {
73-
let pageCallCount = 0
74-
let measurementIds = getIds(pluginConfig.measurementIds)
73+
let pageCallCount = 0;
74+
let measurementIds = getIds(pluginConfig.measurementIds);
7575
const initConfig = {
7676
...defaultConfig,
7777
...pluginConfig,
78-
}
79-
78+
};
79+
8080
return {
81-
name: 'google-analytics',
81+
name: "google-analytics",
8282
config: initConfig,
8383
// Load gtag.js and define gtag
8484
initialize: ({ config, instance }) => {
85-
const { dataLayerName, customScriptSrc, gtagName, gtagConfig, debug } = config
85+
const { dataLayerName, customScriptSrc, gtagName, gtagConfig, debug } =
86+
config;
8687
/* Inject google gtag.js script if not found */
8788
if (!scriptLoaded(customScriptSrc || gtagScriptSource)) {
88-
const customLayerName = (dataLayerName) ? `&l=${dataLayerName}` : ''
89-
const script = document.createElement('script')
90-
script.async = true
91-
script.src = customScriptSrc || `${gtagScriptSource}?id=${measurementIds[0]}${customLayerName}`
92-
document.body.appendChild(script)
89+
const customLayerName = dataLayerName ? `&l=${dataLayerName}` : "";
90+
const script = document.createElement("script");
91+
script.async = true;
92+
script.src =
93+
customScriptSrc ||
94+
`${gtagScriptSource}?id=${measurementIds[0]}${customLayerName}`;
95+
document.body.appendChild(script);
9396
}
9497
/* Set gtag and datalayer */
9598
if (!window[dataLayerName]) {
96-
window[dataLayerName] = window[dataLayerName] || []
97-
window[gtagName] = function() {
98-
window[dataLayerName].push(arguments)
99-
}
100-
window[gtagName]('js', new Date())
99+
window[dataLayerName] = window[dataLayerName] || [];
100+
window[gtagName] = function () {
101+
window[dataLayerName].push(arguments);
102+
};
103+
window[gtagName]("js", new Date());
101104
}
102105
// Initialize tracker instances on page
103106
let gtagConf = {
104107
...defaultGtagConf,
105-
...(gtagConfig) ? gtagConfig : {},
106-
...(debug) ? { debug_mode: true } : {},
108+
...(gtagConfig ? gtagConfig : {}),
109+
};
110+
// You must explicitly delete the debug_mode parameter or all sessions will fire in debug more. Setting it false is not enough.
111+
// https://support.google.com/analytics/answer/7201382?hl=en&ref_topic=9303319#zippy=%2Cgoogle-tag-websites:~:text=To%20disable%20debug%20mode%2C%20exclude%20the%20%27debug_mode%27%20parameter%3B%20setting%20the%20parameter%20to%20false%20doesn%27t%20disable%20debug%20mode.
112+
if (debug === true) {
113+
gtagConf.debug_mode = true;
114+
} else {
115+
delete gtagConf.debug_mode;
107116
}
108117
/* set custom dimensions from user traits */
109-
const user = instance.user() || {}
110-
const traits = user.traits || {}
118+
const user = instance.user() || {};
119+
const traits = user.traits || {};
111120
if (Object.keys(traits).length) {
112-
window[gtagName]('set', 'user_properties', traits)
121+
window[gtagName]("set", "user_properties", traits);
113122
}
114123
/* Initialize all measurementIds */
115124
for (var i = 0; i < measurementIds.length; i++) {
116125
if (!loadedInstances[measurementIds[i]]) {
117-
window[gtagName]('config', measurementIds[i], gtagConf)
118-
loadedInstances[measurementIds[i]] = true
126+
console.log("a", gtagConf);
127+
window[gtagName]("config", measurementIds[i], gtagConf);
128+
loadedInstances[measurementIds[i]] = true;
119129
}
120130
}
121131
},
122132
// Set parameter scope at user level with 'set' method
123133
identify: ({ payload, config }) => {
124-
const { gtagName } = config
125-
if (!window[gtagName] || !measurementIds.length) return
134+
const { gtagName } = config;
135+
if (!window[gtagName] || !measurementIds.length) return;
126136
if (payload.userId) {
127137
// https://developers.google.com/analytics/devguides/collection/ga4/user-id?platform=websites#send_user_ids
128-
window[gtagName]('set', { user_id: payload.userId })
138+
window[gtagName]("set", { user_id: payload.userId });
129139
// console.log('Set userid', payload.userId)
130140
}
131-
// TODO verify this
141+
// TODO verify this
132142
// https://developers.google.com/analytics/devguides/collection/ga4/user-properties?technology=websites
133143
if (Object.keys(payload.traits).length) {
134144
/* gtag('set', 'user_properties', {
135145
favorite_composer: 'Mahler',
136146
favorite_instrument: 'double bass',
137147
season_ticketholder: 'true'
138148
}) */
139-
window[gtagName]('set', 'user_properties', payload.traits)
149+
window[gtagName]("set", "user_properties", payload.traits);
140150
// console.log('Set userprops', payload.traits)
141151
}
142152
},
143153
// Set parameter scope at page level with 'config' method
144154
page: ({ payload, config, instance }) => {
145-
const { gtagName, gtagConfig } = config
146-
if (!window[gtagName] || !measurementIds.length) return
147-
const { properties } = payload
148-
const { send_to } = properties
149-
const campaign = instance.getState('context.campaign')
155+
const { gtagName, gtagConfig } = config;
156+
if (!window[gtagName] || !measurementIds.length) return;
157+
const { properties } = payload;
158+
const { send_to } = properties;
159+
const campaign = instance.getState("context.campaign");
150160
// console.log('ga page properties', properties)
151161
/* Create pageview-related properties */
152162
const pageView = {
@@ -156,38 +166,38 @@ function googleAnalytics(pluginConfig = {}) {
156166
page_hash: properties.hash,
157167
page_search: properties.page_search,
158168
page_referrer: properties.referrer,
159-
}
160-
const campaignData = addCampaignData(campaign)
169+
};
170+
const campaignData = addCampaignData(campaign);
161171
const finalPayload = {
162-
...(send_to) ? { send_to } : {},
172+
...(send_to ? { send_to } : {}),
163173
...pageView,
164174
...campaignData,
165-
}
175+
};
166176
/* If send_page_view true, ignore first analytics.page call */
167177
if (gtagConfig && gtagConfig.send_page_view && pageCallCount === 0) {
168-
pageCallCount++
178+
pageCallCount++;
169179
// console.log('ignore first pageCallCount', pageCallCount)
170-
return
180+
return;
171181
}
172182
// console.log('Send page_view payload', finalPayload)
173-
window[gtagName]('event', 'page_view', finalPayload)
183+
window[gtagName]("event", "page_view", finalPayload);
174184
// Set after initial page view
175-
pageCallCount++
185+
pageCallCount++;
176186
},
177187
// Set parameter scope at event level with 'event' method
178188
track: ({ payload, config, instance }) => {
179-
const { properties, event } = payload
180-
const campaign = instance.getState('context.campaign')
181-
const { gtagName } = config
182-
if (!window[gtagName] || !measurementIds.length) return
189+
const { properties, event } = payload;
190+
const campaign = instance.getState("context.campaign");
191+
const { gtagName } = config;
192+
if (!window[gtagName] || !measurementIds.length) return;
183193
/* Attach campaign data */
184-
const campaignData = addCampaignData(campaign)
194+
const campaignData = addCampaignData(campaign);
185195
// Limits https://support.google.com/analytics/answer/9267744
186196
const finalPayload = {
187197
...properties,
188198
/* Attach campaign data, if exists */
189199
...campaignData,
190-
}
200+
};
191201
/*
192202
console.log('finalPayload', finalPayload)
193203
console.log('event', event)
@@ -197,61 +207,64 @@ function googleAnalytics(pluginConfig = {}) {
197207
<event_params>key: value,
198208
})
199209
*/
200-
window[gtagName]('event', event, finalPayload)
210+
window[gtagName]("event", event, finalPayload);
201211
},
202212
/* Verify gtag loaded and ready to use */
203213
loaded: () => {
204-
const { dataLayerName, customScriptSrc } = initConfig
205-
const hasDataLayer = dataLayerName && (window[dataLayerName] && Array.prototype.push === window[dataLayerName].push)
206-
return scriptLoaded(customScriptSrc || gtagScriptSource) && hasDataLayer
214+
const { dataLayerName, customScriptSrc } = initConfig;
215+
const hasDataLayer =
216+
dataLayerName &&
217+
window[dataLayerName] &&
218+
Array.prototype.push === window[dataLayerName].push;
219+
return scriptLoaded(customScriptSrc || gtagScriptSource) && hasDataLayer;
207220
},
208221
/* Custom methods */
209222
methods: {
210223
addTag(tagId, settings = {}) {
211224
// https://developers.google.com/tag-platform/devguides/install-gtagjs#add_products_to_your_tag
212225
if (window[initConfig.gtagName]) {
213-
window[initConfig.gtagName]('config', tagId, settings)
226+
window[initConfig.gtagName]("config", tagId, settings);
214227
// Add tag id
215228
if (measurementIds && !measurementIds.includes(tagId)) {
216-
measurementIds = measurementIds.concat(tagId)
229+
measurementIds = measurementIds.concat(tagId);
217230
}
218231
}
219232
},
220233
/* Disable gtag for user */
221234
disable: (ids) => {
222-
const gaIds = (ids) ? getIds(ids) : measurementIds
235+
const gaIds = ids ? getIds(ids) : measurementIds;
223236
for (var i = 0; i < measurementIds.length; i++) {
224-
const gaId = measurementIds[i]
237+
const gaId = measurementIds[i];
225238
if (gaIds.includes(gaId)) {
226239
// https://developers.google.com/analytics/devguides/collection/gtagjs/user-opt-out
227-
window[`ga-disable-${gaId}`] = true
240+
window[`ga-disable-${gaId}`] = true;
228241
}
229242
}
230243
},
231244
/* Enable gtag for user */
232245
enable: (ids) => {
233-
const gaIds = (ids) ? getIds(ids) : measurementIds
246+
const gaIds = ids ? getIds(ids) : measurementIds;
234247
for (var i = 0; i < measurementIds.length; i++) {
235-
const gaId = measurementIds[i]
248+
const gaId = measurementIds[i];
236249
if (gaIds.includes(gaId)) {
237250
// https://developers.google.com/analytics/devguides/collection/gtagjs/user-opt-out
238-
window[`ga-disable-${gaId}`] = false
251+
window[`ga-disable-${gaId}`] = false;
239252
}
240253
}
241-
}
254+
},
242255
},
243-
}
256+
};
244257
}
245258

246259
function getIds(measurementIds) {
247-
if (!measurementIds) throw new Error('No GA Measurement ID defined')
260+
if (!measurementIds) throw new Error("No GA Measurement ID defined");
248261
if (Array.isArray(measurementIds)) {
249-
return measurementIds
262+
return measurementIds;
250263
}
251-
if (typeof measurementIds === 'string') {
252-
return [ measurementIds ]
264+
if (typeof measurementIds === "string") {
265+
return [measurementIds];
253266
}
254-
throw new Error('GA Measurement ID must be string or array of strings')
267+
throw new Error("GA Measurement ID must be string or array of strings");
255268
}
256269

257270
/**
@@ -264,21 +277,23 @@ function getIds(measurementIds) {
264277
* @param {String} [campaignData.campaignKeyword] - Keyword of campaign
265278
*/
266279
function addCampaignData(campaignData = {}) {
267-
let campaign = {}
268-
const { id, name, source, medium, content, keyword } = campaignData
269-
if (id) campaign.campaignId = id
270-
if (name) campaign.campaignName = name
271-
if (source) campaign.campaignSource = source
272-
if (medium) campaign.campaignMedium = medium
273-
if (content) campaign.campaignContent = content
274-
if (keyword) campaign.campaignKeyword = keyword
275-
return campaign
280+
let campaign = {};
281+
const { id, name, source, medium, content, keyword } = campaignData;
282+
if (id) campaign.campaignId = id;
283+
if (name) campaign.campaignName = name;
284+
if (source) campaign.campaignSource = source;
285+
if (medium) campaign.campaignMedium = medium;
286+
if (content) campaign.campaignContent = content;
287+
if (keyword) campaign.campaignKeyword = keyword;
288+
return campaign;
276289
}
277290

278291
function scriptLoaded(scriptSrc) {
279-
const scripts = document.querySelectorAll('script[src]')
280-
const regex = new RegExp(`^${scriptSrc}`)
281-
return Boolean(Object.values(scripts).filter((value) => regex.test(value.src)).length)
292+
const scripts = document.querySelectorAll("script[src]");
293+
const regex = new RegExp(`^${scriptSrc}`);
294+
return Boolean(
295+
Object.values(scripts).filter((value) => regex.test(value.src)).length
296+
);
282297
}
283298

284-
export default googleAnalytics
299+
export default googleAnalytics;

0 commit comments

Comments
 (0)