GTM Dev Guide

Transcription

GTM Dev Guide
ClientDomain.com
"Generic" GTM Developer Guide
ID: ClientDomain_GTM_Dev_Guide_v4.15
Author: Phil Pearce
Date: 24th Jan 2014
Please download the latest version of this document here:
http://bit.ly/gtmdevguide
If you have suggestions for improving this document please email:
[email protected]
Many Thanks!
Phil Pearce
Senior Web Analyst
[email protected]
http://uk.linkedin.com/in/philpearce
Change Log:
th
1. Updated process tree on page4 [PP on 16 Sept].
2. Added transaction_currency=GBP for global rollup profile and
th
transaction_currency_secondTracker=USD for Local currency profile [PP on 28 Sept].
rd
3. Added auto-event PDF download, mailto & outbound link tracking [PP on 3 Oct].
rd
4. Added the new on/off GTM debug mode which outputs to firebug console [PP on 3 Oct].
5. Added brandName, country, region, externalCrossDomainlinksToDecorate,
isExternalLinkTrackingEnabled, isDownloadLinkTrackingEnabled,
th
isMailtoLinkTrackingEnabled and CD020|CM020 to global header dataLayer [PP on 5 Oct].
6.
7.
8.
9.
10.
11.
th
Added "growth of GTM page" [PP on 6 Oct].
th
Fixed typo on gtm.blacklist which requires uses "[arrays]" even for single values [PP on 6 Oct].
th
Added GTM "version number" in source code screenshot [PP on 16 Oct].
th
Added gtm.whitelist and updated gtm.blacklist [PP on 20 Oct].
nd
Added position of GTM code examples [PP on 2 Nov].
Fixed typo "event":"doneWithQubitMapped_trackTrans" from onload to push as all customHTML
th
dataLayer updates must use push.[PP on 5 Nov].
th
12. Added Google+ social tracking for Universal (native in legacy GA) [PP on 7 Nov].
13. Commented-out settings_ga_setDomainName as Analytics.js defaults to TLD now. Only ga.js needs this
14. Changed social plugin dataLayer names, due to a Google change.
st
15. Fixed Qubit to GTM mapping code issue [PP on 1 Dec Sept].
th
16. Added page_title into virtual URL links for modals and outclicks [PP on 9 Dec Sept].
th
17. Added remarketing example & re-order document and move detailed aspects into appendix [PP 9 Dec].
18. IMPORTANT: Updated CustomDimensions with userScope=6months, sessionScope=30mins,
pageScope=pageOnly (e.g. user_cd001_userScope_isNewRegistration) and pageGrouping with
th
appended variableName (e.g. page_group1_category) [PP on 18 Dec Sept].
th
19. Updated W3C list with V1.0 digitalData dataLayer Names spec [PP on 18 Dec Sept].
th
20. Updated the dc.js fallback with an event capture (e.g. 6% pageviews fallback) [PP on 1 Jan 2013].
th
21. Added tip about seamless migration of old code into GTM customHTML using an event. [16 Jan 2013].
th
22. Added manual video tracking example with.. if(!window.dataLayer) {dataLayer = [];} [PP 17 Jan 2013].
th
23. Add Dedduracell Tomi`s WordPress plugin [PP 24 Jan 2013].
Feature request list: (tweet @philpearce to add new stuff)
1. Add iterations & timelines page.
2. Provide HTML examples to accompany this guide using a preview & debug mode link.
3. Provide Demo GTM account login to accompany this guide in read-only mode.
4. Add Youtube API auto-tracking script library.
5. Add Facebook and Twitter auto-tracking script library.
6. Add Claudia Kosny`s GTM iframe tracking script.
7. Add Mark Rochefort`s JS macro library.
1
8. Add Doug Hall`s naming convention page.
9. Add David Hefendehl script for using GTM to deploy A/B test or Content Experiment code.
10. Add ?gtm_debug_console=true {{macro}} tip.
11. Add settings_ga_isJSErrorLoggerScriptEnabled=true to include jslogger.com or muscula.com
12. Add more JSON digitalData examples by Qubit:
WYSIWYG tool to generate markup: tools.qubitproducts.com/uv/developers/code/
Contents
Growth & adoption of GTM ...................................................................................................... 3
Executive Summary ................................................................................................................... 4
The process ................................................................................................................................. 5
Comparison of GA setup vs GTM setup .............................................................................. 5
List of Installation Questions to ask ...................................................................................... 6
Common GTM setup mistakes & checks ............................................................................ 7
Installation - position of the dataLayer and gtm.js.............................................................. 8
Installation - development environment setup .................................................................... 9
Logins & permissions............................................................................................................ 10
Debug process....................................................................................................................... 11
Chrome Tag Assist Plugin - MUST be installed ............................................................... 12
GA accountID`s for DEV and LIVE ..................................................................................... 14
GTM scripts examples ............................................................................................................. 15
Important note about inline code: ....................................................................................... 15
Ecommerce Transactions on Sale complete pages ........................................................ 18
Social Measurement: Social Actions buttons.................................................................... 21
Item page tracking ................................................................................................................. 23
Inline page error tracking ..................................................................................................... 24
Modal and Ajax box tracking: .............................................................................................. 25
Outclick tracking (via jQuery) .............................................................................................. 26
Onsite search ......................................................................................................................... 27
404 Error Tracking ................................................................................................................ 28
Custom Variables: Registered logged-in users ................................................................ 29
Custom Variables: Customer Groupings ........................................................................... 29
Event Tracking: user classification based on form fields ................................................ 30
Auto-event PDF & outclick tracking examples.................................................................. 30
List of QA test ........................................................................................................................ 32
Appendix .................................................................................................................................... 33
Links to other resources ....................................................................................................... 33
More Debugging tips:............................................................................................................ 34
Tip about seamless migrating global includes code to GTM.......................................... 37
Security checklist ................................................................................................................... 38
GTM settings screenshot ..................................................................................................... 39
Plugin: Adwords Custom/Dynamic Remarketing ............................................................. 40
ga.js auto-cross domain tracking for LINKS (beta script) ............................................... 42
ga.js auto-cross domain tracking form POSTS (beta script) .......................................... 46
Plugin: Civic cookie consent notification script ................................................................. 48
Plugin: Prevent GA ecommerce counting twice (Courtesy Brian Kuhn) ...................... 49
Plugin: GTM for Magento (Thanks to Chris Martin) ......................................................... 50
Plugin: GTM for Wordpress ................................................................................................. 51
Plugin: Qubit for Wordpres .................................................................................................. 52
Plugin: GTM for Drupal ......................................................................................................... 52
Plugin: GTM for Joomla........................................................................................................ 52
GTM & Flash videos ............................................................................................................. 53
GTM & iFrames ..................................................................................................................... 54
GTM & noscript tracking ....................................................................................................... 54
Work-around for tag firing order dependencies ................................................................ 55
Customisation: Mapping Qubit dataLayer to GTM ecommerce dataLayer .................. 56
Customisation: CustomHTML fallback script for dc.js ..................................................... 59
Cheat sheet of #digitalData JSON Object Names (v1.0.1) ................................................... 65
Map W3C names to GTM names - used to pass validation ........................................... 67
2
Growth & adoption of GTM
GTM is rapidly being adopted and it is used by some of the largest website on the internet:
http://trends.builtwith.com/widgets/Google-Tag-Manager
Only Adobe Tag Manager (free) is growing all other TMS are remaining static or declining:
GTM is on about 4% of the top 10K of website on October 2013
Hence there is a need to deploy this effectively and standardise GTM installations... hence this
GTM developer guide was born :)
3
Executive Summary
Once the KPI`s document has been delivered, we can instruct the developer to install
& test these measurements on the development environment.
Business
Objectives
Workshop
Objectives >
KPI`s
Example
KPI`s Doc
Create
Recommend
Vendor
Solution
Deploy
•a container
•the "empty" container
•the website to what you want to track
Map
Dev Guide
Configure
•the container in GTM settings interface
Installation
•in debug & preview mode
Test
QA testing
Migrate
•by simultaneously removing hard
coded tags & publishing the container
Training
Analysis &
Optimisation
4
The process
Tasks & milestones for the planned GTM installation are shown below:
Create
Deploy
Map
Configure
Test
Migrate
• a container
• the "empty" container
• the website to what you want to track
• the container in GTM settings interface
• in debug & preview mode
• by simultaneously removing hard coded tags & publishing the container
Comparison of GA setup vs GTM setup
GA setup:
GTM setup:
5
Tip: If lots of "new" link elements need to be tracked following an installation, then a process
document can be used. For example: http://tatvic.form2go.com/73856.html
List of Installation Questions to ask
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
Is this a New Installation (no historic data) or Existing GA Installation (historic data)
If it is an existing is upgrade to Universal GA setting going to be enabled?
Is client aware that the GA legacy to Universal upgrade is a one-way process (i.e irreversible)
Is the client currently reliant on GA data? (or is an existing system such as Omniture et al being
primarily used for business reporting)
Is the client running in fast migration sprints or single monthly releases (e.g. global header,
then ecommerce page, then events & customVars)?
Is the client rolling out on one domain first or all-in-one-go?
Is client happy to running Beta code?
Is adding the code to the top <body> tag a problem? Or can only the global header or footer
be changed?
Is it necessary to access GA client-side cookie values at time of a form submission or for
Behavioural content such as getCustomVar(1) or utmz values?
Are more than 5 custom variables required at time of installation?
Is integration with CRM required?
Does the client have a requirement to track multi-device journeys from registered users who
are logged-in from Website to logged-in within App?
Is the client using rolledup trackers on other domains which are using ga.js?
Is resetting the New/Returning visitor cookie a problem? (If ga.js and analytics.js are both
present this does not happen, only on analytics.js only installations)
Does the client need to integrate Adwords Remarketing using GA, GA to DoubleClick exporting
& Content Experiments or AdSense revenue importing.
https://support.google.com/analytics/answer/2795983?hl=en-GB
Does the client need to integrate with widgets such as Google+, AddThis, ShareThis, Disqus,
Optimizely, LiveChat or PhoneCall tracking providers which only (currently) support legacy
GA.js
Is cross-domain or iframe tracking required (easier in universal).
Is native GooglePlus button tracking needed (requires code in Universal).
Is it a problem if all Referrals within a session reset the session in UA? For example, if a visitor
is on your site, leaves but then immediately returns, this visitor has logged two sessions. You
can, however, modify your tracking code to exclude all traffic from specific domains as referral
traffic with the referral exclusions.
UPDATE: Universal Analytics profile views now default to ignore clientdomain.com as selfreferral e.g. ['_addIgnoredRef', 'clientdomain.com'] thus, this problem is resolved.
https://support.google.com/analytics/answer/2795830?hl=en
GA Outcomes
1. GA.js only
2. Universal Analytics.js only
3. Both GA.js AND Universal Analytics.js in parallel (most likely outcome)
4. GA.js pageTracker & GA.js secondTracker in parallel
Remarketing Outcomes
1. Adwords remarketing (URL based remarketing + Custom/Dynamic Remarketing)
2. dc.js remarketing using GA classic (Only supports URL based remarketing)
3. dc.js remarketing using Universal private beta (Only supports URL based remarketing)
6
Common GTM setup mistakes & checks
1. No migration plan e.g.
a. straight switch or parallel mode?
b. if parallel - secondTracker or GA universal?
c. migration sprints or single-release (e.g. global header, then ecommerce page, then events
& customVars)?
d. one domain first, or all-in-one-go?
e. GTM for app aswell?
2. No standardised and consistent naming conventions or W3C valid dataLayer.
3. Capitalisation issue on dataLayer names & dataLayer values (very common)
4. No separate GTM Dev vs Live environment (GTM-dev, GTM-live) or environment_Identifier.
5. No GA accountID for Dev (UA-xxxxx-2)
6. No allocated client development resources or IT buy-in, or IT dept on-boarding process.
7. GTM code not previewed/tested before publishing.
8. Chrome Tag Assist not used to debugging
a. Unescaped commas & single quotations in dataLayer by client`s developers.
b. GTM placed in nested <div>
i. Severe IE7 bug if gtm installed in nested <div> or in <head> and blocking rule of
navigator.userAgent matches RegEx ^.+ \(.*MSIE 7\.0; .*\).*$
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
not added on custom HTML.
c. GTM iframe placed in <head> rather than <body> causing IE warning.
setDomainName dataLayer not present.
a. mixing setDomainName="www.clientdomain.com" & "clientdomain.com" which are
different cookie domain hashes.
b. fallback default value setDomainName="auto" not enabled, causing tracking to fail.
event_action or element_class missing and fallback default value, causing events to fail.
setting_ga_id dataLayer not used, thus migrations from Dev to Live break the GA accountID.
a. Hardcode UA-xxxx-1 used in customHTML rather than {{settings_ga_id}}
Hardcode gaq.push used rather than dataLayer.push within customHTML.
AddThis, ShareThis, Disqus, Optimizely, LiveChat all use default pageTracker. Causing GA to trigger
UA-xxxxx-1 errors.
When using secondTrackers, the {{trackerName}} macro must be used in customHTML, otherwise
GA to will trigger UA-xxxxx-1 errors.
If using Content Experiments and cross-domain tracking ensure
<script>_udn = "{{dl_settings_ga_setDomainName}}";</script> is added.
Rollback to Empty container button is hidden, if more than 6 containers.
Poor Google Account security:
a. Password easy to guess via brute force
b. Two-stage SMS authentication not enabled
c. "gtm.blacklist":["customScripts","nonGoogleScripts","nonGoogleIframe
s","k"] fallback not considered
dataLayer=[]; used in CustomHTML onload - when a dataLayer.push({}); must be used to append
(rather than override values). See this discussion here.
Local currency field not-set in localTracker: ga('create', '{{settings_ga_id}}',
{'name':'localTracker'}, 'set', 'currencyCode', '{{transaction_currency}}');
More mistakes avoid here: Get the dataLayer Right by Josh West (WebAnalyticsDemystified) and
GA to Universal migration checklist by Jordan Louis (CardinalPath) & Common pitfalls by Qubit.
7
Installation - position of the dataLayer and gtm.js
Some CMS`s are not built with a global top <body> include (only a global header or
footer include) or there is a need to do a like-for-like replacement of GA in the header
for GTM in the header. Thus, here are some possible alternatives:
11.
2.
2
1
13.
3
14.
4
1
DataLayer in <head> and GTM in top <body> (2 include files)
Both DataLayer & GTM in top <body> (1 include file)
Both DataLayer & GTM javascript in <head>, with GTM iframe in footer (2
include files)*
Both DataLayer & GTM javascript in the <head>, with GTM iframe not used (1
include file)*
*Note1: If customHTML is used, then a blocking-rule/patch for IE7 must be added
for example: navigator.userAgent JavaScript variable which matches RegEx ^.+
\(.*MSIE 7\.0; .*\).*$
*Note2: GTM team does not test GTM in <head> thus there is no guarantee that it
will continue to work the future.
Existing GA setup:
Existing
GTM setup options:
1
2
1
1
4
1
3
1
8
Installation - development environment setup
When setting up a DEV and LIVE environment there are 3 solutions, depending on the
complexity of your installation, flexibility of your server and your attitude towards risk.
Note: At the time of writing - GTM does not support an external API for source control,
thus configurations need to be manually copied. However, tags can be copied, and
iMacro browser automation can be used instead.
11. Two separate GTM-dev and GTM-dev accountID, and ask the client to insert a
server-side switch (safest option: but requires manual work to copy/sync containers) :
1
<?php
switch ($_SERVER['HTTP_HOST']) {
case 'localhost':
case 'dev.clientdomain.com':
case 'staging.clientdomain.com':
$gtm_account_id = 'GTM-DEV'; // Dev GTM
break;
case 'www.clientdomain.com':
$gtm_account_id = 'GTM-LIVE'; // LIVE GTM
break;
default:
$gtm_account_id = 'GTM-LIVE'; // default to LIVE
}
?>
<!-- Google Tag Manager container for GA and Adwords -->
<noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-<?php echo
$gtm_account_id ?>"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-<?php echo $gtm_account_id ?>');</script>
<!-- End Google Tag Manager -->
22. One GTM-container, but with a prepended identifier of "dev|live"
1 (less dangerous option).
dev_<TagType>_<TagName>_<TagID>
e.g. dev_GA_classic_pageview_UA-000000-2
live_<TagType>_<TagName>_<TagID>
e.g. live_GA_classic_pageview_UA-000000-1
Then use hostname include/exclude rule
or better use: "environmentIndentifer"="dev|staging|live";
33. One GTM-container on both live and dev and just use preview & debug extensively!
1 (most dangerous option)
Related post on this discussion is here.
9
Logins & permissions
You will need to create a GTM account and container here:
https://www.google.com/tagmanager/
username: [email protected]
password: choose-your-password
Once created you will need to add [email protected] to GTM
with full publish access.
AnalyticsAgency recommend all GTM account with delete or upload
access have
2stage authentication via sms or iphone app to enhance security.
support.google.com/accounts/bin/answer.py?hl=en&answer=180744 and
www.youtube.com/watch?v=zMabEyrtPRg
10
Debug process
How to avoid getting burned by tags here.
How to debug video
We recommend your developers watch this short video on how GTM works:
http://www.youtube.com/watch?v=KRvbFpeZ11Y&list=PLFwbZmNsefUvq930NjgBoQeTUX6foIhP
11
Chrome Tag Assist Plugin - MUST be installed
It is a requirement that developers install the chrome plugin to validate the GTM code:
https://chrome.google.com/webstore/detail/tag-assistant-bygoogle/kejbdjndbnbjgmefkgdddjlbokphdefk?hl=en
It is recommended to set chrome plugin to "Detailed" mode, rather than "Basic" mode:
12
Here is an example of the Chrome validator in action:
Clicking on the "not working" shows details:
Tip: JSON validator is really good for checking the dataLayer
http://jsonlint.com/ for example
{
"variableName": "textValue",
"variableName": "textValue"
}
Note: jsonlint.com does not like HTML comments, and single-quotes need to be
changed to double-quotes in order to validate.
13
GA accountID`s for DEV and LIVE
AnalyticsAgency have setup GTM with a live account (GTM-DEV) and staging account
(GTM-LIVE). For Example:
Important note: Regarding the GA and GTM accountIDs, these need to be set on the
page. For example:
<!-- Google Tag Manager container for GA -->
<script>dataLayer = [{
"settings_ga_id": "UA-000000-2",
"settings_ga_id_secondTracker": "UA-111111-2",
// ...
}];
</script></head>
...
<body><!-- Google Tag Manager container for GA -->
<noscript>
<iframe src="//www.googletagmanager.com/ns.html?id=GTM-xxx
...></iframe>
</noscript>
<script>
... window,document,'script','dataLayer','GTM-xxx');
</script>
If the GA_ID is empty (e.g. "settings_ga_id": "") then GTM will gracefully fallback
to:
14
GTM scripts examples
ClientDomain will insert the appropriate GTM container script tag at the top of the
page directly after the opening <body> tag and not within any nested <div> an example
is shown on the next page.
As referenced earlier the GTM-accountID`s are:


DEV GTM account is GTM-DEV (dev.ClientDomain.com)
LIVE GTM account is GTM- LIVE (www.ClientDomain.com)
For login and sale complete pages the null custom variables and transaction dataLayer
variables will need to be populated.
Important note about inline code:
In order to get the most benefit from GTM, avoid running inline GA code.
Instead, using jQuery to append onclick events on-the-fly to buttons (via GTM) will
mean that the Analytics code becomes independent from your CMS and thus futureproof.
It is imperative that jQuery is placed above GTM at the top of the page and a
standardised version (e.g. v10.1) of jQuery is used throughout the website:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js">
</script>
DOM elements need to be named in a consistent and robust way. This is because the
tracking code is tightly coupled to DOM attributes. If classes or IDs of DOM elements
that are tracked are changed tracking functionality maybe effected.
For example:
ID attributes on div elements
<div id="gtm-action" name="xxx"><a href="#"></div>
CLASS attributes
<a href="#" class="gtm-action">
HTML5 data attributes
<a href="#" data-gtm="action">
Phone number markup:
<span itemprop="tel gtm-number-replace">
<a href="tel:+44123400000">+44 (0)1234 00000</a>
</span>
Form markup for sensitive fields:
<input id="CCNum" type="text" class="gtm-sensative ClickTaleSensitive">
Note: Do not change any existing data variables declared in the page header as they
can be referenced by the GTM code. For example:
"window.isLoggedIn" = "true";
"window.isKnownUser" = "true";
Important reminder: The existing GA code should not be removed yet.
15
<!-- Existing dataLayer - used to turn ServerSide variables into JS -->
<script>
// "window.isLoggedIn" = "true"; // Global variables
// "window.isKnownUser" = "true";
</script>
<!-- Set dataLayer for visitors GeoIP country - WARNING non-ssl script -->
<script src="http://www.geoplugin.net/javascript.gp?ver=3.5.1" type="text/javascript">
</script>
<script src="http://www.geoplugin.net/extras/cookielaw.js" type="text/javascript"></script>
<!-- jQuery 1.10.1 loaded via Google CDN with optional jquery-migrate backward compatibility -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script>window.jQuery || document.write("<script src='\/\/ClientDomain.com\/js\/common\/jquery\/jquery1.10.1.min.js'><\/script>");</script><script>jQuery.noConflict();</script>
<script src="//code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<!-- Adwords Dynamic Remarketing tag for product image IDs support.google.com/adwords/answer/3103357 &
support.google.com/tagmanager/answer/3002580 & support.google.com/tagmanager/answer/3002580 -->
<script type="text/javascript">
// Product Page values
var google_tag_params = {
"ecomm_pagetype": "", // home|category|searchresults|product|basket|purchase|other|siteview
"ecomm_prodid": "", // e.g. 123
"ecomm_totalvalue": [""], // e.g 100.00 pounds
"ecomm_pname": [""], // optional - use arrays for multiple products on same page
"ecomm_pcat": [""], // optional - use arrays for multiple products on same page
"ecomm_rec_prodid": [""], // related productID e.g. shoe shine
// Auth logged-in or Ecommerce variables
"a": "20-25",
// Users Age range
"g": "m",
// Users Gender
"hasaccount": "y", // users has an account
"cqs": "1",
// 1-3 users Customer Quality Score
"rp": "y", // y|n users Repeat Purchaser
"hs": "1", // 1-3 users Loyalty Score
"ly": "1" // 1-3 users High Spender Score
};
</script>
<!-- Google Tag Manager dataLayer for global header -->
<script>
dataLayer = [{
// GA settings
"version": "1.0", // W3C digital dataLayer version
"environment_Identifier": "dev", // dev, staging or live
"settings_ga_isDebugConsoleEnabled": "true", // only enable on dev
// "gtm.blacklist": ["customScripts","nonGoogleScripts","nonGoogleIframes","k"],// Uncomment to disable
custom JS for debugging purposes
"settings_ga_id": "UA-000000-2", // ROLLEDUP UA-000000-2 on DEV, replace UA-000000-1 on LIVE
"settings_ga_id_secondTracker":"UA-111111-2", // LOCAL UA-111111-2 on DEV, replace UA-111111-1 on LIVE
"settings_ga_setDomainName": "clientdomain.com", // if using ga.js or dc.js MUST be set to TopLevelDomain,
this is not needed if just using analytics.js
"settings_ga_brandName": "big brand", // If using multiple brands in rollup then use this field
"settings_ga_country": "UK", // Use 2character ISO Country list
"settings_ga_locale": "", // locale-code for page language e.g. en-US
"settings_ga_region": "", // Only needed for large localised websites
"settings_ga_externalCrossDomainlinksToDecorate": "otherdomain1.com, mycart.com", //
"settings_ga_isInPageLinkTrackingEnabled": "false", // Improves accuracy of internal Link tracking
"settings_ga_isOutboundLinkTrackingEnabled": "true", // Exit links to other websites
"settings_ga_isDownloadLinkTrackingEnabled": "true", // PDF tracking
"settings_ga_isMailtoLinkTrackingEnabled": "true", //
"settings_ga_isHashUrlFragmentsTrackingEnabled":"false",//Append location.hash page_virtual#url
"settings_ga_isScrollTrackingEnabled": "false", // Blog content pages customHTML script
"settings_ga_isYoutubeTrackingEnabled": "false", // Youtube API auto-tracking customHTML script
"settings_ga_isFacebookAndTwitterButtonTrackingEnabled": "false", //Auto-social button tracking
// Visitors preference and GEO-ip settings
"visitor_preferenceForDNT": window.navigator.doNotTrack, // 1|0|"not-set" beta JS dom function
"user_auth_enableUserIDtoSessionIDoveride": "false", // true|false is set on Login or Register complete
page. userIDoveride MUST default to false. Only change if consent gained or DNT=0
"visitor_geoplugin_status": geoplugin_status, // 403 error, 200 is lookup ok
"visitor_geoIpCountryCode": geoplugin_countryCode, // geoplugin JS variable
"visitor_geoIpContinentCode": geoplugin_continentCode, // geoplugin JS variable
"visitor_geoIPisCookieConsentRequired": geop1ugin_cookieConsent, // JS variable
"visitor_xPreview": "<?php if ($_SERVER['HTTP_X_PURPOSE'] == 'preview' {'preview'} else {''} ?>", // Safari
loading page in preview mode
// Page values
"page_virtual": "", // e.g. "/virtual"+location.pathname+location.search
"page_attributes_SysEnv": "desktop", // Responsive CSS = desktop | tablet | mobile.
"page_httpResponseCode": "<?php http_response_code(); ?>", // e.g 200 or 404 or 500
"page_pageID": "1234",
"page_siteSearchResults": "", // 0 is 0 results returned
"page_group1_category": "",
// page category
"page_group2_subCategory": "", // page sub-category
"page_group3_templateName": "",// template name
"page_group4_funnelStep": "", // funnel number or funnel step name
"page_group5_author": "",
// page author
"page_contentCreated": "",
// 2013-01-01
"page_contentModified": "",
// 2013-01-01
"page_forumPosts": "",
// e.g. 25
16
// Ecommerce payment page variables
"transaction_date": "", // optional
"transaction_promoCode": "", // optional
"transaction_paymentType": "", // optional
"transaction_currency": "", // Global currency rollup profile
"transaction_currency_secondTracker": "", // Local currency profile
"transactionId": "",
"transactionAffiliation": "",
// optional
"transactionTotal": "", // 120*1quantity + 120*1quantity + 10shipping
"transactionTax": "", // 250 * (1-(100%/120%))
"transactionShipping": "", // 8.33 + tax
"transactionProducts": [
{"sku":"", "name":"", "category":"", "price":"", "quantity":""},
{"sku":"", "name":"", "category":"", "price":"", "quantity":""}
],
// Trigger transaction submission to GA via GTM
// "event": "trackTrans",
// Adwords settings
"google_conversion_id": "",
"google_conversion_label": "",
//"google_conversion_value": "", // same as dataLayer[3].transactionTotal
// CustomDimensions persistence can be userScope=6months, sessionScope=30mins or hitScope=pageScope
"user_cd001_userScope_isNewRegistration": "", // customVariable001
"user_cd002_userScope_isUserNewCustomer": "", // customVariable002
"user_cd003_userScope_isLoggedIn": "",
// customVariable003
"user_cd004_userScope_class": "",
// customVariable004
"user_cd005_userScope_userID": "",
// customVariable005 - aka Universal auth.user.getId
"user_cm001_currencyType_visitorLifetimeValue": "" // EventValue or CustomMetric value
}];
</script>
<!-- End Google Tag Manager Data Layer -->
</HEAD>
<BODY>
<!-- Google Tag Manager container - Used to deploy ga.js and analytics.js -->
<!-- VERSION: GTM_Jan_v1 -->
<!-- WEBSITE: www.clientdomain.com -->
<!-- CATEGORY: ecommerce -->
<!-- MARKET: UK -->
<!-- GTM-LIVE: UA-000000-1 -->
<!-- GTM-DEV: UA-000000-2 -->
<!-- Replace GTM-xxxx with GTM-DEV for live or GTM-LIVE for dev -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-xxxx');
</script>
<noscript>
<iframe src="//www.googletagmanager.com/ns.html?id=GTM-xxxx" height="0" width="0"
style="display:none;visibility:hidden">
</iframe>
</noscript>
<!-- End Google Tag Manager -->
17
Ecommerce Transactions on Sale complete pages
AnalyticsAgency will set GTM to only trigger on event=trackTrans OR page is the sale complete
thankyou page, they will be ignored on other pages if they are set in the global header.
Do not remove the existing GA ecommerce code yet:
<script type="text/javascript">
// Existing GA ecommerce code on /checkout/thankyou.htm
var _gaq = _gaq || [];
_gaq.push(['_setDomainName','clientdomain.com'],['_setAccount','UA-000000-2']);
_gaq.push(['_addTrans',
'T000012345', // Order_ID
'Aff123',
// Affiliation
'130.00',
// Revenue - Inc VAT and Shipping
'20.00',
// Vat Tax @ 20%
'10.00',
// Shipping - Inc VAT
'','',''
// Depreciated Region, City & Country
]);
_gaq.push(['_addItem',
'T000012345', // Order_ID
'BS123',
// ProductSKU or ProductID - required
'Blue Shoes', // ProductName - Pls \escaped quotes & commas
'Shoes',
// prod_category - Pls \escaped quotes & commas
'120.00',
// Price Inc VAT @ 20%
'1'
]);
_gaq.push(['_trackTrans']); // submit transaction
</script>
On the sale complete pages /checkout/thankyou.htm insert the following dataLayer
making sure that the variables displayed in bold are changed appropriately.
<!-- Google Tag Manager dataLayer for Sale page only -->
<script>
dataLayer = [{
// GA settings
"version": "1.0", // W3C digital dataLayer version
"environment_Identifier": "dev", // dev, staging or live
"settings_ga_isDebugConsoleEnabled": "true", // only enable on dev
// "gtm.blacklist": ["customScripts","nonGoogleScripts","nonGoogleIframes","k"],// Uncomment to disable
custom JS for debugging purposes
"settings_ga_id": "UA-000000-2", // ROLLEDUP UA-000000-2 on DEV, replace UA-000000-1 on LIVE
"settings_ga_id_secondTracker":"UA-111111-2", // LOCAL UA-111111-2 on DEV, replace UA-111111-1 on LIVE
"settings_ga_setDomainName": "clientdomain.com", // if using ga.js or dc.js MUST be set to TopLevelDomain,
this is not needed if just using analytics.js
"settings_ga_brandName": "big brand", // If using multiple brands in rollup then use this field
"settings_ga_country": "UK", // Use 2character ISO Country list
"settings_ga_region": "", // Only needed for large localised websites
"settings_ga_externalCrossDomainlinksToDecorate": "otherdomain1.com,mycart.com", //
"settings_ga_isInPageLinkTrackingEnabled": "false", //
"settings_ga_isOutboundLinkTrackingEnabled": "true", //
"settings_ga_isDownloadLinkTrackingEnabled": "true", //
"settings_ga_isMailtoLinkTrackingEnabled": "true", //
"settings_ga_isHashUrlFragmentsTrackingEnabled":"false",//
"settings_ga_isScrollTrackingEnabled": "false", //
"settings_ga_isYoutubeTrackingEnabled": "false", //
"settings_ga_isFacebookAndTwitterButtonTrackingEnabled": "false", //
// Visitors preference and Geo-ip settings
"visitor_preferenceForDNT": window.navigator.doNotTrack, // 1|0|"not-set" beta JS dom function
"user_auth_enableUserIDtoSessionIDoveride": false, // true|false is set on Login or Register complete page.
userIDoveride MUST default to false. Only change if consent gained or DNT=0
"visitor_geoplugin_status": geoplugin_status, // 403 error, 200 is lookup ok
"visitor_geoIpCountryCode": geoplugin_countryCode, // geoplugin JS variable
"visitor_geoIpContinentCode": geoplugin_continentCode, // geoplugin JS variable
"visitor_geoIPisCookieConsentRequired: geop1ugin_cookieConsent, // JS variable
"visitor_xPreview": "<?php if ($_SERVER['HTTP_X_PURPOSE'] == 'preview' {'preview'} else {''} ?>", // Safari
loading page in preview mode
18
// Page values
"page_virtual": "", // e.g. "/virtual" + location.pathname + location.search
"page_attributes_SysEnv": "desktop", // Responsive CSS = desktop | tablet | mobile.
"page_httpResponseCode": "<?php http_response_code(); ?>", // e.g 200 or 404 or 500
"page_pageID": "1234",
"page_siteSearchResults": "", // 0 is 0 results returned
"page_group1_category": "",
// page category
"page_group2_subCategory": "", // page sub-category
"page_group3_templateName": "",// template name
"page_group4_funnelStep": "", // funnel number or funnel step name
"page_group5_author": "",
// page author
"page_contentCreated": "", // 2013-01-01
"page_contentModified": "", // 2013-01-01
"page_forumPosts": "", // e.g. 25
// Ecommerce payment page variables
"transaction_date": "2013-01-01", // optional
"transaction_promoCode": "VOUCHER1234", // optional
"transaction_paymentType": "Direct Debit", // optional
"transaction_currency": "GBP", // Global currency rollup profile
"transaction_currency_secondTracker": "USD", // Local currency profile
"transaction_subtotal_include_tax": "true", // Indicates whether Transaction Total includes tax
"transactionId": "T000012345",
"transactionAffiliation": "Aff123",
// optional
"transactionTotal": "250.00", // 120*1quantity + 120*1quantity + 10shipping
"transactionTax": "75.00", // 250 * (1-(100%/120%))
"transactionShipping": "10.00", // 8.33 + tax
"transactionProducts": [
{"sku":"BS123","name":"BLUE Shoes","category":"Shoes", "price":"120.00","quantity":"1"},
{"sku":"RS123","name":"RED Shoes", "category":"Shoes", "price":"120.00","quantity":"1"}
],
// Trigger transaction submission to GA via GTM
"event": "trackTrans",
// Adwords settings
"google_conversion_id": "123456",
"google_conversion_label": "purchase", // enter Adwords label string here or default to "purchase"
//"google_conversion_value": "250.00", // same value as dataLayer.transactionTotal
// CustomDimensions persistence can be userScope=6months, sessionScope=30mins or hitScope=pageScope
"user_cd001_userScope_isNewRegistration": "", // customVariable001
"user_cd002_userScope_isUserNewCustomer": "", // customVariable002
"user_cd003_userScope_isLoggedIn": "",
// customVariable003
"user_cd004_userScope_class": "",
// customVariable004
"user_cd005_userScope_userID": "",
// customVariable005 - aka Universal auth.user.getId
"user_cm001_currencyType_visitorLifetimeValue": "" // EventValue or CustomMetric value
}];
</script>
<!-- End Google Tag Manager Data Layer -->
</HEAD>
<BODY>
<!-- Google Tag Manager container - Used to deploy ga.js and analytics.js -->
<!-- VERSION: GTM_Jan_v1 -->
<!-- WEBSITE: www.clientdomain.com -->
<!-- CATEGORY: ecommerce -->
<!-- MARKET: UK -->
<!-- GTM-LIVE: UA-000000-1 -->
<!-- GTM-DEV: UA-000000-2 -->
<!-- Replace GTM-xxxx with GTM-DEV for live or GTM-LIVE for dev -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-xxxx');
</script>
<noscript>
<iframe src="//www.googletagmanager.com/ns.html?id=GTM-xxxx" height="0" width="0"
style="display:none;visibility:hidden">
</iframe>
</noscript>
<!-- End Google Tag Manager -->
IMPORTANT: Do not use commas in number (e.g. 1,000 change to 1000)
"transactionTotal": "1,000.00", // comma error
Should be:
"transactionTotal": "1000.00",
Note1: Do NOT use null for empty text values
"transactionAffiliation": "NULL", // value error
Instead use empty strings:
"transactionAffiliation": "",
19
Note2: For empty numbers, ensure these are in Quotation marks to avoid a JS error:
"transactionTotal": , // JS error
These are fine:
"transactionTotal": "",
Note3: For empty number values do NOT use "0":
"transactionTotal": "0", // value error
"transactionTax": "0",
change to:
"transactionTotal": "",
"transactionTax": "",
Note4: Both these methods are valid for numbers, but due to JS errrors with null numbers - it is safer to
standardise use quotation marks on populated number values e.g. "100.00":
"transactionTotal": 100.00,
"transactionTotal": "100.00", // safer, but not JSON compliant
Tip: JS trick - you can convert a string to number by simply subtracting 0 e.g:
var myNum = dataLayer["transactionTotal"]-0;
Help page:
 support.google.com/tagmanager/answer/3002596?hl=en
 support.google.com/tagassistant/answer/3207128?hl=en#dl_quoted
 jsonlint.com
20
Social Measurement: Social Actions buttons
There are some well written instructions on GTM social tracking by Simo Ahava here &
the official Google dataLayer names for social here.
In order to track Like & Tweet buttons on the blog and item pages GA measurements
need to be triggered, for example: ['_trackSocial', network, socialAction,
opt_target, opt_pagePath]
In order to track these social interactions the buttons just add customHTML triggered
on gtm.dom ready
Facebook (JavaScript version)
<script>
FB.Event.subscribe("edge.create", function(targetUrl) {
dataLayer.push({
'event': 'social'
'network': 'facebook',
'socialAction': 'like',
'opt_target': targetUrl, // optional
'opt_pagePath': location.pathname + location.search, // optional
'event_value': parseInt(0.00)
});
});
</script>
Twitter (JavaScript version)
<script>
function trackTwitter(intent_event) {
if (intent_event) {
var opt_pagePath;
if (intent_event.target && intent_event.target.nodeName == "IFRAME") {
opt_target = extractParamFromUri(intent_event.target.src, "url");
}
dataLayer.push({
'event': 'social',
'network': 'twitter',
'socialAction': 'tweet',
'opt_target': opt_target, // optional
'opt_pagePath': opt_pagePath, // optional
'event_value': parseInt(0.00)
});
}
}
//Wrap event bindings - Wait for async js to load
twttr.ready(function (twttr) {
//event bindings
twttr.events.bind("tweet", trackTwitter);
});
</script>
Google+ (For Universal this is NOT needed for Legacy GA.js)
<script>
function sendPlus(targetUrl) {
dataLayer.push({
'event': 'social',
'network': 'google',
'socialAction': '+1',
'opt_target': targetUrl.href, // optional
'opt_pagePath': location.pathname + location.search, // optional
'event_value': parseInt(0.00)
});
};
</script>
See this Google documentation for details of the Facebook and Twitter API.
https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingSocial#facebook
https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingSocial#twitter
21
Facebook (Static Link version)
<a href="http://www.facebook.com/ClientDomain" target="_blank">
<img src="/images/social_network/icon_facebook.jpg">
</a>
Hardcoded onclick code can be added...
<a href="http://www.facebook.com/ClientDomain" target="_blank"
onclick="dataLayer.push({'event':'social', 'network':'twitter',
'socialAction': 'tweet', 'opt_target':location.href,
'opt_pagePath':location.pathname+location.search,
'event_value':parseInt(0.00)});">
<img src="/images/social_network/icon_facebook.jpg">
</a>
Please note: for AddThis no action is needed as I this can be added via GTM:
<script type="text/javascript">
var addthis_config = {
"data_ga_property": "{{settings_ga_id}}",
"data_ga_social": true
};
</script>
Help page:
http://support.addthis.com/customer/portal/articles/381260-google-analytics-integration#how
22
Item page tracking
Item page interaction tracking requires the use of events.
For example, when "View on Map" and "Area info" links are clicked within an item
detail page no Google Analytics measurement is currently recorded:
Event tracking and virtual pageviews will be auto appended (via jQuery) to the
following functions via class names:
<div id="listing_header">
<ul id="listing_tools">
<li class="email">
<a title="Send to a friend"
href="/flatshare/tellafriend.pl?advert_id=2586839">Send to a friend</a>
</li>
<\div>
23
Inline page error tracking
Where it is not possible to extract the event via jQuery, then the event must be inserted
via inline code. For example form validation errors:
An example of an inline event is:
<script>
dataLayer.push({
"event_action": "09_password_error",
"event_label": "error_details",
"event": "form_error"
});
</script>
An multiple inline errors occur on the same page the event action should be prepended
with a number: 09_password_error.
24
Modal and Ajax box tracking:
If the URL does not change then this interaction is not recorded in GA, unless the
following code is added. An example of this would be:
Thus ClientDomain`s developers will need to add the following code:
<a href="#" onclick="dataLayer.push({
'event': 'onclick_<xxxx>', // change <xxxx> to onclick_modal
'event_category': 'onclick_<xxxx>', //same as dataLayer.push.event
'event_action': 'click',
'event_label': 'tick_confirm_box',
'event_value': parseInt(0.00),
'event_nonInteractive': false, // Is this an onload action? Y|N
'page_virtual': '/virtual/modal/modal_title/link1',
'page_title': 'modal_title'
}); return false;">Modal Link</a>
Note: the page that an event was triggered on, is stored by default within GA here.
Manual Video tracking:
If the Vimeo, KuluValley or BlueBillywigg Video are used (rather than Youtube) then
these hosted video player will need be configured to trigger GTM events via onclick or
onload actions. These actions can then be send into a firtTracker or secondTracker
using GTM tag settings.
<!-- GTM onclick video play -->
<a href="#" class="gtm-video" onclick="dataLayer.push({
'event': 'video',
'event_category': 'video', //same as dataLayer.push.event
'event_action': 'play', // play pause, stop, share etc
'event_label': '<videoID>_<video name>',
'event_value': parseInt(0.00), //percentage video played 100 to 0
'event_nonInteractive': false, // Is video auto-played? True|False
'page_virtual': location.pathname + location.search,
'page_title': '<video name>'
});">Click here to Play video</a>
<!-- GTM onload video play -->
<script>
if(!window.dataLayer) { dataLayer = []; }
dataLayer.push({
'event': 'video',
'event_category': 'video', //same as dataLayer.push.event
'event_action': 'play', // play pause, stop, share etc
'event_label': '<videoID>_<video name>',
'event_value': parseInt(0.00), //percentage video played 100 to 0
'event_nonInteractive': true // Is video auto-played? True|False
}); </script>
If the video is hosted externally, ask the Video provider if these server-side settings can be added...
And
if(!window.dataLayer) { dataLayer = []; } dataLayer.push({
'settings_ga_id': '<UA-000000-2>', // UA-000000-2 DEV, UA-000000-1 on LIVE
'settings_ga_id_secondTracker': '<UA-111111-2>', // UA-111111-2 DEV , UA-111111-1 on LIVE
'settings_ga_setDomainName': '<clientdomain.com>' // If not entered default to value of 'auto'
});
25
<noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-<xxxx>"
... })(window,document,'script','dataLayer','GTM-<xxxx>');</script>
Once the GA and GTM accountID are set the 'event': 'video' can then be called.
Outclick tracking (via jQuery)
Note: GTM-auto event tracking can also be used rather than this jQuery method on
page 30.
If the same version of jQuery is used throughout the website, then
No developer action is required, as auto Outclick tracking will be added via GTM.
This works by using use jQuery to inspect the href and then attached onclick events to
these links.
Example links:
<div>
<a href="http://www.otherdomain.com/" target="_blank">
Link</a>
</div>
AnalyticsAgency will set GTM will auto insert the following via jQuery :
<div>
<a href="http://www.otherdomain.com/"
onclick="dataLayer.push({
'event': 'onclick_<xxx>', // change <xxx> to onclick_outclick
'event_category':'onclick_<xxx>',//same as dataLayer.push.event
'event_action': 'sub-cat',
'event_label': 'label',
'event_value': parseInt(0.00),
'event_nonInteractive': false, // Is onload action? Y|N
'page_virtual': '',
'page_title': ''
});" target="_blank">
Link</a>
</div>
AnalyticsAgency will add a 100ms delay using SetTimeout will be added to these link
to increase tracking accuracy, and prevent race condition.
setTimeout(
function()
{document.location.href = link.href;},
100);
}
See this help file for details of setTimeout:
http://support.google.com/analytics/bin/answer.py?answer=1136920
26
Onsite search
Currently the search results and search category are recording but we suggest making
two changes:
1. There are two methods to enable internal search results tracking:
a) change the URL:
www.clientdomain.com/search.html?submit=search
&site_search={KEYWORD}
&search_category={CATEGORY} e.g. /shoes/
&num_results={9}
OR
b) add a virtual pageview to the page`s dataLayer:
<script>
dataLayer = [{
"page_virtual": "/virtual" +
"/search.html?submit=search&site_search={KEYWORD}&search_category
={CATEGORY}&num_results={9}", //lowercase
"page_pageCategory": "site-search"
}];
</script>
2. In order to record a “0 results” the code below needs to be added:
Using GTM this will need to display as below. Replace the elements in bold with server-side values.
<script>
dataLayer = [{
"page_siteSearchResults": "0"
}];
</script>
27
404 Error Tracking
1. Ensure the Page Title for 404 pages is:
2. Add GTM code to these 404 page templates:
blog.clientdomain.com/page-that-does-not-exist
www.clientdomain.com/page-that-does-not-exist
[Optional] To get extra detail on 404 errors you can enable this script to the 404
templates:
<script>
dataLayer = [{
"page_httpResponseCode": "<?php http_response_code(); ?>", // e.g 404
"page_virtual": "/virtual/error_404/?error_page=" + location.pathname
+ location.search +"&errorpage_from=" + document.referrer,
"page_pageCategory": "error 404"
}];
</script>
28
Custom Variables: Registered logged-in users
Custom variables are attached to a visitors GA session. They last for 30mins or 6
months. These variables can be set at the point where the visitor is identified (or
logged-in).
<!-- dataLayer for CustomDimensions used to turn ServerSide variables into JS -->
<script>
dataLayer = [{
// CustomDimensions persistence can be userScope=6months, sessionScope=30mins or hitScope=pageScope
"user_cd001_userScope_isNewRegistration": "", // customVariable001
"user_cd002_userScope_isUserNewCustomer": "", // customVariable002
"user_cd003_userScope_isLoggedIn": "",
// customVariable003
"user_cd004_userScope_class": "",
// customVariable004
"user_cd005_userScope_userID": "",
// customVariable005 - aka Universal
auth.user.getId
"user_cm001_currencyType_visitorLifetimeValue": "" // EventValue or CustomMetric value
}];
</script>
<!-- End Google Tag Manager Data Layer -->
Custom Variables: Customer Groupings
As users logs into ClientDomain.com they will be "tagged" with a visitor level custom
variable that will denote them as registered users on the page after login:
<script>
dataLayer = [{
"user_cd003_userScope_isLoggedIn": "loggedin_true" // customVariable003
}];
</script>
29
Event Tracking: user classification based on form fields
Event tracking provides an unlimited number of slots for interactions to be recorded.
No action is required on this section. AnalyticsAgency will add the following using
jQuery or auto-event element classes.
<script type="text/javascript">
jQuery(".emailnotify").on("click", function(e) {
onclick="dataLayer.push({
'event': 'onclick_<xxx>', // change <xxx> to onclick_unsubscribe
'event_category':'onclick_<xxx>',//same as dataLayer.push.event
'event_action': 'click',
'event_label': 'tick_email_unsubscribe',
'event_value': parseInt(0.00),
'event_nonInteractive': true, // Is onload action? Y|N
'page_virtual': ''
});"
});
</script>
Note: the page URL that an event was triggered on, is stored by default within GA
here.
Auto-event PDF & outclick tracking examples
Step1: Create a 2 new tag triggered all pages these will enable two new functions:
gtm.linkClick & gtm.timer
And
Step2: Add a new macro called "element url" & "element classes"
30
Rule1: PDF download:
{{event}} equals gtm.linkClick
{{element url}} ends with .pdf
Rule2: Outbound links:
{{event}} equals gtm.linkClick
AND {{element url}} starts with http (This prevents href="#")
AND {{element url}} does not contain mysite.com
Rule3: Mailto links:
{{event}} equals gtm.linkClick
{{element url}} starts with mailto:
Rule4: 30second event Heartbeat for blog or publishers:
{{event}} equals gtm.timer
Note: Auto Timer is set to 30,000ms one only trigged once.
Rule5: Buttons:
{{event}} equals gtm.linkClick
AND {{element classes}} contains button
Note: Auto Event Class macro will need to be added and the class will need to
match the name of the button.
31
List of QA test
These are the functions that will be tested.















Ajax pages virtual pageviews
OnSubmit/Onclick button tracking
Outclick link tracking (e.g. clicks on links to Amazon book)
Social tracking (via Facebook/Twitter API)
Login/Register custom variable
Visitor profiles segmentation
Anonymous & known visitor identification and onsite behavioural analysis
Campaign tracking
Lead scoring (if applicable)
Social engagement
PDF downloads
Multi-link attribution (multiple links on the same page)
Site search
Key pages
o Product details
o Register
Tab usage (e.g. product details tabs or reivews)
32
Appendix
Links to other resources
Public Help forum:
http://productforums.google.com/forum/#!categories/tag-manager/working-with-google-tag-manager
GooglePlus Private Group (250 member)
https://plus.google.com/u/0/communities/104865292981489764063
DataLayer testing script with callbackListener to validate read/write values
https://github.com/google/data-layer-helper#data-layer-helper-library
Google Developer Guide (not as detailed as this guide):
https://developers.google.com/tag-manager/devguide
FAQ page:
https://www.google.com/tagmanager/faq.html
Help articles:
https://support.google.com/tagmanager/
https://support.google.com/tagmanager/answer/3002596?hl=en&ref_topic=3002579
Blog posts:
http://cutroni.com/blog/category/tag-management-2/
http://cutroni.com/blog/2012/05/14/make-analytics-better-with-tag-management-and-a-data-layer/
33
More Debugging tips:
Debug mode can be enabled to see GA processes in firebug console log:
Then, in firebug you will see an GA event log & error messages:
This script can now be triggered automatically in preview and debug mode:
34
GTM will validate JavaSript within customHTML whenever a version is created, but if
you want to enable inline validation this free chrome plugin can be installed here:
Tip: Your container version number is embedded in the source of gtm.js - this is helpful
when you're not sure which version you are testing:
http://www.googletagmanager.com/gtm.js?id=GTM-xxxx
Tip: You can also output the container number into a customDimension using this new
macro.
35
Tip: When Previewing & debugging you can turn off display:none via chrome inspect
elements and you will see the tags JS code. Read more here.
Tip: There is a useful blog post about other methods of GTM debugging &
troubleshooting here.
36
Tip about seamless migrating global includes code to GTM
When moving hardcoded tags into GTM, the hard code can be replaced with an event
named fire_<name>_<trackerType>_tracking for example:
<!-- Omniture PAGEVIEW tracker WAS here moved into GoogleTagManager -->
<script>
dataLayer.push({"event": "fire_omniture_pageview_tracking"});
</script>
Then a rule needs to be added into GTM to trigger this customHTML.
The advantage of this method is that it means that when the old code is replaced with
GTM it works seamlessly.
The same method applies to DoubleClick code, except page specific values can also
be passed in:
<!-- DoubleClick COUNTER was here - moved into GoogleTagManager
Creation Date: 01/01/2013 -->
<script>
dataLayer.push({
"event": "fire_doubleclick_counter_tracking",
"src": "3018669",
"type": "atter993",
"cat": "ithom544",
"ord": "1"
});
</script>
37
Security checklist
1. To disabled customHTML inorder to enhance security (but restrict features) enable this
JSON object via custom HTML:
<script>
dataLayer.push({
"gtm.blacklist":
["customScripts","nonGoogleScripts","nonGoogleIframes","k"]
});
// Help page: https://developers.google.com/tag-manager/devguide#security
</script>
Note1: Blacklisting individual script also works using
"gtm.blacklist": ["html","jsm","k","flc", "mpm"], //Disable
CustomHTML to enhanced security BUT restrict features
Note2: to prevent new iFrame Template tags being enabled in GTM such as the new
Mediaplex IFRAME Tag, you can use a whitelist instead. For example
"gtm.whitelist":["ga", "ua", "img", "awct", "sp", "fls", "ms", "cts"]
Note3: daisy chaining events to enable gtm.blacklist (via customHTML) and
event=pageview is not necessary, as gtm.blacklist is loaded BEFORE other
customHTML is triggered. However, as the customHTML gtm.blacklist block can easily
be disabled within GTM, adding hardcode for gtm.blacklist is more secure, if you need
to enable this function. Also if added via customHTML a dataLayer push MUST be
used.
2. Initially, Marketing team should be set with read only access with preview changes.
IT should be set to with publish to dev/live access.
http://support.google.com/tagmanager/answer/2695756/?hl=en&topic=2574304&ctx=topic
3. The Google Accounts can be set to prompt to reset the password every 30days.
4. Two-stage authentication can be enabled on Google Account with access to GTM.
5. Google Webmaster Tools has an email alert me if it detects malware on a website
crawl.
6. The GA community is very active with regards to reporting malware and GTM blocking
issue, these normally get picked up very quickly.
Also see this post on more GTM security tips:
https://plus.google.com/117298997433687198127/posts/jFgd1J8Trqz?cfem=1
An overview of why IT love GTM is here:
http://static.googleusercontent.com/external_content/untrusted_dlcp/www.google.co.uk/en/uk/tagmanag
er/pdfs/google-tag-manager-technical-factsheet.pdf
38
GTM settings screenshot
39
Plugin: Adwords Custom/Dynamic Remarketing
To remarket based on customer types or basket value you need to enable this, rather
than URL base remarketing. Note: Remarketing using Univeral.js is still in private beta.
Depending on page pathname change



pagetype=PURCHASE
pagetype=BASKET
pagetype=PRODUCT
Help pages



support.google.com/adwords/answer/310335
support.google.com/tagmanager/answer/3002580
www.dropbox.com/s/d883lxracxijf3j/Adwords%20Remarketing%20Tag%20with%20custom%20parameters
%20via%20GTM%20-%20Andr%C3%A9%20Mafei.pdf
Discussion post is here:
First add a JavaScript Variable:
Then map to this in the GTM remarketing tag:
40
This remarketing tag will need to be triggered on:
Then add this custom HTML triggered on /success/ OR /cart/ or /product/ page:
<script>
// Adwords Remarketing Custom Parameters
var uri = window.location.pathname;
var remarketing_page_type = null;
var google_tag_params = [];
if ((uri.indexOf('/') == 0) || (uri.indexOf('/index') == 0)) {
remarketing_page_type= 'home';
} else if (uri.indexOf('/nsearch/') == 0) {
remarketing_page_type= 'searchresults';
} else if (uri.match('^\/.+\-[0-9]{4,20}/?(\?.+)?$') == 0) {
remarketing_page_type= 'products'; // Assuming product pages are /xxx-1234
} else if (uri.match('^\/(category_1|category_2)\/?([a-zA-Z0-9_\-]+|\?.+)$') == 0) {
remarketing_page_type= 'category';
} else if (uri.indexOf('/checkout/cart/') == 0) {
remarketing_page_type= 'basket';
} else if (uri.indexOf('/checkout/onepage/') == 0) {
remarketing_page_type= 'checkout';
} else if (uri.indexOf('/checkout/onepage/success/') == 0) {
remarketing_page_type= 'purchase';
} else {
remarketing_page_type= 'other';
}
// Adwords Custom Remarketing mapping
window.google_tag_params = {
ecomm_prodid: "[{{dl2_transactionProducts_sku}}]", // MUST be same as GoogleProducts
xml Feed
ecomm_pagetype: remarketing_page_type, //
home|category|searchresults|product|basket|purchase|other|siteview
ecomm_totalvalue: {{dl_transactionTotal}}, // e.g 100.00 dollars change to basket
value or poduct value
pname: "[{{dl2_transactionProducts_name}}]", // optional - use arrays for multiple
products on same page
pcat: "[{{dl2_transactionProducts_category}}]", // optional - use arrays for multiple
products on same page
// Auth logged-in or Ecommerce variables
"a": "",
// Users Age range e.g. 20-25
"g": "",
// Users Gender e.g. m|f
"hasaccount": "{{dl_visitorLoginState}}",
// users has an account or is loggedin
"rp": "{{dl_visitorExistingCustomer}}",
// y|n users Repeat Purchaser
"hs": "{{dl_visitorLifetimeValue}}",
// 1-3 users Loyalty Score or
userValueOverTime
"ly": "",
// 1-3 users High Spender Score
"cqs": ""
// 1-3 users Customer Quality Score
};
// Now run Adwords remarketing code
dataLayer.push({"event": "fire_rmkt_google_tag_params"});
</script>
41
ga.js auto-cross domain tracking for LINKS (beta script)
For universal analytics cross-domain tracking - just add the internal domains within
GTM settings (or add a line into the dataLayer to update this setting:
"settings_ga_autoLinkCrossDomains":"myotherdomain1.com, mycart.com").
https://support.google.com/tagmanager/answer/3561401
To implement cross-domain tracking with the Google Analytics template, follow the
steps below.
Note: Since the reaction to a user clicking on a link is directing the user to another
URL, you will only have time to really tie the GA event to the tracked event. If you tie
other tags to the cross_link event, they may not have time to fire.
Warning: You must do all of these steps at the same time in order for it to work. If you
add the HTML code but don't add the corresponding Link trackers from within the GA
template, your links will be broken. Check for broken links by using Preview Mode.
42
1. Create a new Google Analytics tag of the type "Cross-domain tracking for Link" named
GA crossdomain SCRIPT for LINKS with all the same settings as the Google
Analytics Pageview tag. This must have AllowLinker checked and the same Domain
Name settings as other tags.
2. In the same tag template, in the Target URL field macro drop-down, create a new
macro called targetUrl, type Data Layer macro, and set the value to targetUrl.
43
3. Now that you've created the {{targetUrl}} macro, make sure that the cross-domain
tracking tag has the Target URL field set to {{targetUrl}}.
4. Create a new rule to fire the Cross-domain Link tag when an event matching cross_link
occurs.
5. Create a new Custom HTML tag containing the provided script below, entering all
domains across which you want to measure a visitor's interactions as a single website
in the 'whitelist' array. Make sure this tag fires on a new rule All pages + gtm.dom (This
means creating a new rule where {{url}} matches RegEx .* and {{event}} equals
gtm.dom).
44
6. Create and publish a new container version.
<!-- CustomHTML: GA crossdomain SCRIPT for LINKS -->
<script type="text/javascript">
window._gaq = window._gaq || [];
var whitelist = [
'externaldomain2.com', // add other domains here
'externaldomain2.com',
'www.externaldomain2.com',
'www.externaldomain2.com'
];
/**
* Checks if the link's hostname matches the whitelist.
* You can customise this to use regular expressions instead of substrings.
* By default 'google.co.uk' & 'www.google.com' substrings matched separately.
*/
function isMatch(whitelisted, linkHostname) {
return linkHostname.indexOf(whitelisted) >= 0;
}
////////////////////////////////////////////////
// No need to modify anything below this line.
////////////////////////////////////////////////
function isCrossDomain(link) {
if (link.hostname != '' && link.hostname != document.domain) {
for (var i = 0; i < whitelist.length; i++) {
if (isMatch(whitelist[i], link.hostname)) return true;
}
}
return false;
}
var allLinks = document.getElementsByTagName('a');
for (var i = 0; i < allLinks.length; i++) {
if (isCrossDomain(allLinks[i])) {
allLinks[i].onclick=function(){
dataLayer.push({
'targetUrl': this.href,
'linkTarget': this.target,
'event': 'cross_link'
});
return false;
}
}
}
</script>
45
ga.js auto-cross domain tracking form POSTS (beta script)
1. Add a new custom HTML called GA cross domain SCRIPT for form POSTS with the
following script:
<!-- CustomHTML: GA crossdomain SCRIPT for form POSTS -->
<script>
function listen(obj, type, listener, opt_useCapture) {
var w3c = 'addEventListener',
ie = 'attachEvent';
if (obj[w3c]) obj[w3c](type, listener, !! opt_useCapture);
else if (obj[ie]) obj[ie]('on' + type, listener);
}
var myforms = document.getElementsByTagName('form');
for (i=0; i<myforms.length; i++) {
listen(myforms[i], 'submit', function (event) {
dataLayer.push({
'formObject': this,
'event': 'formSubmitted'
});
});
}
</script>
2. Then add this macro:
46
4. Then add this rule
5. Then add this tag template set to :
47
Plugin: Civic cookie consent notification script
<body>
<!-- Civic code to paste into GTM custom HTML -->
<script type='text/javascript' src='/wp-content/plugins/cookie-control/js/cookieControl5.1.min.js?ver=3.5.1'>
</script>
<script type="text/javascript">
jQuery(document).ready(function() {
cookieControl({
introText:"<p>Deze website maakt gebruik van cookies. Lees ons pricavy beleid. <a
href='http://clientdomain.com/privacy-statement/'>Privacy Policy.</a></p>",
fullText:"<p>Sommige cookies zijn noodzakelijk om te zorgen dat deze website functioneert.
Bijvoorbeeld bij het invullen en versturen van een formulier. We maken ook gebruik van analyse
cookies. Deze verzamelen anonieme data zodat wij onze website kunnen verbeteren. Social media
buttons kunnen ook cookies plaatsen.</p>",
position:'left',
shape:'triangle',
theme:'dark',
startOpen:true,
autoHide:6000,
subdomains:'false',
consentModel:'implicit',
onAccept:function(){},
onReady:function(){},
onCookiesAllowed:function(){},
onCookiesNotAllowed:function(){},
countries:''
});
});
function ccAddAnalytics(){
// Run GTM event to trigger GA pageview
dataLayer.push({
"page_virtual": "", // If set to null default pageURI used
"event": "pageview"
});
};
</script>
CookieControl can be set to load once the page has loaded using gtm.dom ready:
For facebook social buttons you would use...
<script>
function ccAddSocial(){
// onclick social event example
FB.Event.subscribe('edge.create', function(targetUrl) {
dataLayer.push({
"socialAction": "like",
"event_label": "facebook",
"event_value": parseInt(0.00),
"event": "social"
});
});
});
</script>
48
Plugin: Prevent GA ecommerce counting twice (Courtesy Brian Kuhn)
Step 1) add this custom HTML:
<script>
// CustomHTML only trigger on /checkout/sale-complete.html and gtm.dom ready
function createCookie(name,value,days) {
if (days) { // If not provided, it will be a session cookie.
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}
else var expires = "";
document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
if (!readCookie('cookie_reachedConfirmation')) {
createCookie('cookie_reachedConfirmation', 'true', 1);
}
</script>
Step 2) Create a new first-party-cookie macro called cookie_reachedConfirmation.
Step 3) Create a blocking rule for conversion tracking tag to not fire if cookie_reachedConfirmation=true
49
Plugin: GTM for Magento (Thanks to Chris Martin)
If you are using Magento, this free plugin will auto enable GTM with the dataLayer values. The
GTM-account ID is set within the configuration.
https://github.com/CVM/Magento_GoogleTagManager
http://www.magentocommerce.com/magento-connect/google-tag-manager-6038.html
Note: the plugin does not output "environment_Identifier": "dev" or
google_tag_params (yet).
50
Plugin: GTM for Wordpress
You just need to add the GTM-xxxx ID in Wordpress.
Wordpress does now have a top <body> include, but GTM can be auto-installed in the footer.
http://wordpress.org/plugins/wp-google-tag-manager/stats/
(3,520 downloads, Last Updated 1 year ago: 10th January 2013)
Not tested yet, but.... looks very good, and supports:
 comment events
 remarketing tags
 native scroll tracking
 and inline events for PDF downloads & mailtos
http://wordpress.org/plugins/duracelltomi-google-tag-manager/
(1,848 downloads, Last Updated 1week ago: 17th January 2014)
If you are more technical - use WordPress Metronet plugin instead which has a much better
dataLayer. To install Metronet - modify your theme's header.php to include <?php do_action(
'body_open' ); ?> right after the opening <body> tag. Go to the plugin's settings and input your
Google Tag Manager data.
http://wordpress.org/plugins/metronet-tag-manager/stats/ (485 downloads, Last Updated
6months ago: 30th July 2013)
51
Plugin: Qubit for Wordpres
Qubit have a dataLayer plugin for Wordpress that can be piggybacked:
http://wordpress.org/plugins/qubit-opentag/screenshots/
Plugin: GTM for Drupal
I have not tested this, but this is the most popular Drupal plugin:
https://drupal.org/node/1813730
https://drupal.org/search/site/%22Google%20Tag%20Manager%22
Qubit have a dataLayer plugin for Drupal that can be piggybacked:
https://drupal.org/sandbox/chop/2121889
Plugin: GTM for Joomla
TBC: email me if you know a good one: [email protected]
52
GTM & Flash videos
To enable GTM to fire tags based on content or interactions in a Flash movie, the
ActionScript External Interface API must be used to push Events and dynamic Page
Variables to the dataLayer on the container page from the SWF movie.
To achieve this functionality, the GTM container code snippet should be implemented
within the HTML of the SWF’s parent page. Events and dynamic Page Variables may
then be pushed from the Flash component into GTM by calling the push() API via
External Interface. For example, to trigger an Event upon click of a button
(mybutton_btn) using ActionScript 3, you could implement the following code within
your SWF:
import flash.display.*;
import flash.events.*;
import flash.external.*;
mybutton_btn.addEventListener(MouseEvent.MOUSE_UP,
onButtonClick); function onButtonClick( Event:MouseEvent ):void
{
var name:String= "FLASH_EVENT"; if (ExternalInterface.available)
{
ExternalInterface.call('dataLayer.push',{'event': name}); }}
For the ExternalInterface API to function properly ensure that when embedding your
SWF, script access is enabled:
<object classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'
width='300' height='300' id='player1' name='player1'> <param
name='movie' value='file.swf'> <param name='allowfullscreen'
value='true'> <param name='allowscriptaccess' value='always'>
<param name='flashvars' value='file=playlist.xml'> <embed
id='player1'
name='player1' src='file.swf' width='300' height='300'
allowscriptaccess='always' allowfullscreen='true'
flashvars="file=playlist.xml" /> </object>
53
GTM & iFrames
GTM will generally not function from within an iframe, Since GTMs functionality
depends on access to the DOM.
GTM & noscript tracking
Only about 3% of Browser have JS disabled and legacy ga.js does not run if JS is
disable and Firefox V23 prevents JS being turned off - thus this this step is very much
optional:
<!-- Google Tag Manager NOSCRIPT global tag example -->
<noscript>
<iframe src="//www.googletagmanager.com/ns.html?id=GTMDEV&environment_Identifier=dev&&settings_ga_id=UA-0000002&&settings_ga_id_secondTracker=UA-1111112&&settings_ga_setDomainName=clientdomain.com&&settings_ga_external_sub
site=campaignsite1_com&&visitor_preferenceForDNT=&page_attributes_SysEn
v=desktop&&page_httpResponseCode=<?php http_response_code();
?>&&page_pageID=1234&&page_virtual=&&page_siteSearchResults=&&page_page
Category=&&page_group2=&&page_group3=&&page_group4=&&page_group5=&&page
_contentCreated=&&page_contentModified=&&page_forumPosts=&&user_isNewRe
gistration=&&user_isUserNewCustomer=&&user_isLoggedIn=&&user_class=&&us
er_ID=&&user_visitorLifetimeValue=&&event=trackTrans" height="0"
width="0" style="display:none;visibility:hidden">
</iframe>
</noscript>
<!-- Google Tag Manager NOSCRIPT ecommerce tag example -->
<noscript>
<iframe src="//www.googletagmanager.com/ns.html?id=GTMDEVenvironment_Identifier=dev&&settings_ga_id=UA-0000002&&settings_ga_id_secondTracker=UA-1111112&&settings_ga_setDomainName=clientdomain.com&&settings_ga_external_sub
site=campaignsite1_com&&visitor_preferenceForDNT=notset&page_attributes_SysEnv=desktop&&page_httpResponseCode=<?php
http_response_code();
?>&&page_pageID=1234&&page_virtual=&&page_siteSearchResults=&&page_page
Category=&&page_group2=&&page_group3=&&page_group4=&&page_group5=&&page
_contentCreated=&&page_contentModified=&&page_forumPosts=&&transaction_
promoCode=VOUCHER1234&&transaction_paymentType=Direct
Debit&&transaction_currency=GBP&&transactionId=T000012345&&transactionA
ffiliation=Aff123&&transactionTotal: 250.00&&transactionTax:
75.00&&transactionShipping:
10.00&&sku=BS123&name=BLUE%20Shoes&category=Shoes&price=120.00&quantity
=1&sku=BS123&name=BLUE%20Shoes&category=Shoes&price=120.00&quantity=1&u
ser_isNewRegistration=&&user_isUserNewCustomer=&&user_isLoggedIn=&&user
_class=&&user_ID=&&user_visitorLifetimeValue=&&event=trackTrans"
height="0" width="0" style="display:none;visibility:hidden">
</iframe>
</noscript>
54
Work-around for tag firing order dependencies
Solution:
1. {{url}} matches RegEx .* AND event=gtm.dom
Start TagA -> Fires on dom ready (gtm.dom)
-> sends event "doneWithTagA" to the dataLayer
dataLayer.push({"event": "doneWithTagA"}); // CustomHTML
2. TagB fires on "doneWithTagA"
-> sends event "doneWithTagB" to the dataLayer
dataLayer.push({"event": "doneWithTagB"}); // CustomHTML
3. TagC fires on "doneWithTagB"
-> sends event "doneWithTagC" to the dataLayer
dataLayer.push({"event": "doneWithTagC"}); // CustomHTML
Source: http://www.cardinalpath.com/controlling-tag-firing-order-with-google-tagmanager/
55
Customisation: Mapping Qubit dataLayer to GTM ecommerce
dataLayer
Thanks to Claudia Kosny & Carmen Mardiros for help with this script!
GTM (currently) has fixed dataLayer naming conventions for ecommerce.
If the client is unwilling to replace the Qubit universal dataLayer with a GTM dataLayer then the
following options can be used:
<!-- Qubit universal variable with GTM ecommerce -->
<script type="text/javascript">
//<!-window.universal_variable = window.universal_variable ? window.universal_variable : {};
window.universal_variable = {
"version": "1.1.1",
"page": {
"category": "Confirmation" // Also just "Checkout Confirmation" can be used
},
"transaction": {
"currency": "GBP",
"order_id": "HSS012546",
"total": 414.72,
"tax": 69.12,
"shipping_cost": 4.00,
"voucher": "MYVOUCHER1", // "promo_code" also used
"voucher_discount": 38.4,
"subtotal": 380,
"subtotal_include_tax": false,
"payment_type": "Visa",
"shipping_method": "1st Class Royal Mail",
"line_items": [{
"product": {
"name": "Sparkly Shoes",
"sku_code": "0987654321",
"manufacturer": "The Shoe Co",
"category": "Shoe",
"subcategory": "Heels",
"color": "n/a",
"stock": 3,
"size": "6",
"unit_price": 130,
"unit_sale_price": 130,
"voucher": "MYVOUCHER1" // "promo_code" also used
},
"quantity": 1
}, {
"product": {
"name": "Red Dress",
"sku_code": "0987654321",
"manufacturer": "The Dress Co",
"category": "Dresses",
"subcategory": "Red dresses",
"color": "n/a",
"stock": 3,
"size": "6",
"unit_price": 200,
"unit_sale_price": 150,
"voucher": "MYVOUCHER1" // "promo_code" also used
},
"quantity": 1
}],
"user": {
"user_id": "12134124",
"returning": true
}
}
}
//-->
</script>
56
<!-- customHTML on sale complete page AND event=gtm.load ready for Sale page only -->
<!-- Map Qubit to GTM names: MUST be called AFTER universal_variable.transaction -->
<script>
// Cannot execute loops within an array declaration.
// Thus - first create the products array before pushing it into dataLayer.
var eCommProducts = [],
currProduct, i, ii;
for (i = 0, ii = window.universal_variable.transaction.line_items.length; i < ii; i +=
1) {
currProduct = window.universal_variable.transaction.line_items[i];
eCommProducts.push({
'id'
: window.universal_variable.transaction.currency + "_" +
window.universal_variable.transaction.order_id, // Currency+OrderID
'name'
: currProduct.product.name +"- "+ currProduct.voucher, //
ProductName+voucherCode
'sku': currProduct.product.sku_code+"- "+ currProduct.voucher, //
ProductSKU+voucherCode
'category' : currProduct.product.category,
'price'
: currProduct.product.unit_sale_price,
'quantity' : currProduct.quantity
});
}
// Now declare Ecommerce payment dataLayer variables
dataLayer.push({
'transaction_currency': window.universal_variable.transaction.currency,
'transactionId': window.universal_variable.transaction.currency + "_" +
window.universal_variable.transaction.order_id,
'transactionAffiliation': window.universal_variable.transaction.promo_code,
'transactionTotal': window.universal_variable.transaction.total,
'transactionTax': window.universal_variable.transaction.tax,
'transactionShipping': window.universal_variable.transaction.shipping_cost,
'transactionProducts': eCommProducts // Trigger Loop array above
});
// Send Transaction
dataLayer.push({
'event':'doneWithQubitMapped_trackTrans'
});
</script>
Then add this JS variable macro:
Which is called in this rule here:
Which is called here in the above customHTML tag:
57
Help files:
 http://productforums.google.com/d/msg/tag-manager/_pcSeOzWJsw/JwTiM6Ij1NUJ
 https://developers.google.com/tag-manager/devguide#renaming
 https://github.com/qubitproducts/UniversalVariable#transaction
 http://opentagsupport.qubitproducts.com/help/discussions/problems/219-readinguniversal-variables-ecommerce-code-with-javascript-or-jquery [Qubit deleted this post]
Notes:
 GTM is much better for Adwords remarketing than Qubit
 GTM now supports document.write and a hack can be used for Tag dependencies
using event daisy-chaining.
 JS variables can be used to turn Qubit variables into {{macros}} in GTM.
 For Qubit ecommerce arrays I am hoping that GTM ecommerce template adds macro
support in 2014-Q1. Then the mapping can be done natively without using
customHTML :)
58
Customisation: CustomHTML fallback script for dc.js
Note: AdBlocker plus has now whitelisted doubleclick.net thus this optional step only
necessary if the client operates in a vertical where there is a higher than normal
number of adblockers installed (e.g Adult content website).
To prevent a loss in traffic when switching for ga.js to dc.js caused by adblocking of
doubleclick.net domain, this script can be added:
<!-- Source: andrescholten.net/google-analytics-retargeting-for-adwords-and-adblock-software -->
<script>var adblocking = true;</script>
<script src="/js/advertising.js"></script>
<!-- You will need to upload advertising.js file which just contains var adblocking = false; -->
<script>
//dataLayer = []; RESET dataLayer - ONLY uncomment this if added via hardcode ABOVE gtm.js
// Check to see if adblocking.js was blocked, then load dc.js via GTM event
if (!adblocking) {
dataLayer.push({"event": "all_pages_dc_js", "event_action": "success", "event_label": "dc_js"});
}
// if blocked load ga.js fallback via GTM event
else {
dataLayer.push({"event": "all_pages_ga_js", "event_action": "failure", "event_label": "ga_js"});
}
</script>
<head>
Note pageviews would need to be triggered by 2 event for 'all_pages_dc_js' OR
'all_pages_ga_js', rather than all pages rule.
If you want to track blockage rate you can traigger two non-interactive events based on
"event_action": "success" and "event_action": "failure". For example:
SetTimeOut and if gat == undefined can also be used if you do not want to use
adblocking.js method..
<!-- customHTML script to trigger two events - one using dc.js and the other using ga.js sourced from here -->
<script>// set accountID and setDomainName
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '{{dl_settings_ga_id}}']); //Change your Profile ID here or map it to a Macro
_gaq.push(['_setDomainName', '{{dl_settings_setDomainName}}']); // set to top level domain or "auto"
// Run on event=gtm.load OR wait 1000ms using setTimeout
setTimeout(function(){
if (typeof _gat == 'undefined') {
// Record an onload event of doubleclickTest in GA via dataLayer
dataLayer.push({
'event': 'ifDoubleclickLoadFAILED_LoadGoogleAnalytics_ga_js',
'event_action': 'scriptLoad_FAILED',
'event_nonInteraction': true
});
} else {
dataLayer.push({
'event': 'ifDoubleclickLoadSUCCESS_LoadDoubleClick_dc_js',
'event_action': 'scriptLoad_SUCCESS',
'event_nonInteraction': true
});
}
}, 1000);
})();
</script>
WARNING about blending ga.js and dc.js
Installing different versions of the tracking code on the same page - is not a supported implementation
and will trigger a ChromeTagAssistant critical error.While data will report to your Google Analytics
account, both the pageview and user data will be unreliable. This will result in pageviews/visits overreporting and Display-eligible users potentially under-reporting. Source:
https://support.google.com/tagassistant/answer/3059154?ref_topic=2947092#ga_dc
59
60
61
62
Note about Bing & Omniture document.write code
GTM added support for experimental document.write on 27th August, thus the following
info about Bing & Omniture is not be a problem anymore.
Bing conversion code & Omniture code use a depreciated method called
document.write which is not asynchronous and thus breaks when added into GTM:
Examples:
For Bing the current work around is to either remove the <noscript> and deploy this
via custom HTML:
<!-- Bing Adcentre Conversion Tracking with Revenue -->
<noscript>
<iframe
src="//flex.atdmt.com/mstag/tag/ENTER_BingAdcenterConversionID_HERE/ana
lytics.html?dedup=1&domainId=ENTER_BingAdcenter_domainId_HERE&type=1&ta
xcost={{dl_transactionTax}}&shippingcost={{dl_transactionShipping}}&rev
enue={{dl_transactionTotal}}&actionid=ENTER_BingAdcenter_actionid_HERE"
frameborder="0" scrolling="no" width="1" height="1"
style="visibility:hidden; display:none">
</iframe>
</noscript>
Or use a custom image tag to trigger the Bing tracking pixel:
//flex.atdmt.com/mstag/tag/ENTER_BingAdcenterConversionID_HERE/analytic
s.html?dedup=1&domainId=ENTER_BingAdcenter_domainId_HERE&type=1&taxcost
={{dl_transactionTax}}&shippingcost={{dl_transactionShipping}}&revenue=
{{dl_transactionTotal}}&actionid=ENTER_BingAdcenter_actionid_HERE
63
The mstag.js contains document.write below:
<script type="text/javascript">
if (!window.mstag) mstag = {loadTag: function() {}, time: (new Date()).getTime() };
</script>
<script type="text/javascript" src="//flex.atdmt.com/mstag/site/9fb70167-8824-4ea7-8637a58da54b6dc9/mstag.js" id="mstag_tops">
<script type="text/javascript">
mstag.loadTag(
"analytics",{
dedup: "1",
domainId: "ENTER_BingAdcenter_domainId_HERE",
type: "1",
taxcost: {{dl_transactionTax}},
shippingcost: {{dl_transactionShipping}},
// nonadvertisingcost: "0.00",
revenue: {{dl_transactionTotal}},
actionid: "ENTER_BingAdcenter_actionid_HERE"
})
</script>
Help pages




http://msdn.microsoft.com/en-us/library/bing-ads-campaign-management-campaign-analytics-scripts.aspx#variable
http://advertise.bingads.microsoft.com/en-ca/help-topic/how-to/moonshot_proc_implementca.htm/use-campaign-analytics-to-track-siteactivity
http://community.microsoftadvertising.com/blogs/advertiser/archive/2010/10/14/feature-comparison-series-conversion-amp-campaignanalytics.aspx
http://advertising.microsoft.com/wwdocs/user/en-us/researchlibrary/researchreport/CampaignAnalytics-GetStarted.pdf
64
Cheat sheet of #digitalData JSON Object Names (v1.0.1)
Page
Products
Cart
Transaction
page.pageInfo.pageID
product[n].productInfo.productID
cart.cartID
transaction.transactionID
page.pageInfo.pageName
product[n].productInfo.productName
cart.price.basePrice
transaction.total.basePrice
page.pageInfo.version
product[n].productInfo.description
cart.price.voucherCode
transaction.total.voucherCode
page.pageInfo.sysEnv
product[n].productInfo.manufacturer
cart.price.voucherDiscount
transaction.total.voucherDiscount
page.pageInfo.breadCrumbs
product[n].productInfo.size
cart.price.currency
transaction.total.currency
page.pageInfo.variant
product[n].productInfo.productImage
cart.price.taxRate
transaction.total.taxRate
page.pageInfo.destinationURL
product[n].productInfo.productThumbnail
cart.price.shipping
transaction.total.shipping
page.pageInfo.referringURL
product[n].productInfo.productURL
cart.price.shippingMethod
transaction.total.shippingMethod
page.pageInfo.onsiteSearchTerm
product[n].category.primaryCategory
cart.price.priceWithTax
transaction.total.priceWithTax
page.pageInfo.onsiteSearchResult
product[n].category.subCategory1
cart.price.cartTotal
transaction.total.transactionTotal
page.pageInfo.language
product[n].category.productType
transaction.attributes.paymentMethod
page.pageinfo.country
product[n].attributes.productSeason
cart.attributes.isProductBundle
Component (Widgets)
page.pageInfo.geoRegion
component[n].attributes.socialWidget
User (Profile & Segments)
transaction.item[n].productInfo.productName
page.pageInfo.issueDate
product[n].linkedProduct[n].productInfo
Events
page.pageInfo.effectiveDate
event[n].eventInfo.eventName
user.profile.profileInfo.profileID
transaction.item[n].productInfo.manufacturer
page.pageInfo.expiryDate
event[n].category.primaryCategory
user.profile.profileInfo.userName
transaction.item[n].category
page.pageInfo.author
event[n].eventInfo.eventAction
user.profile.address
transaction.item[n].price
page.pageInfo.publisher
event[n].eventInfo.type
user.profile.social.facebook
transaction.item[n].quantity
page.pageInfo.industryCodes
event[n].eventInfo.eventPoints
user.profile.social.facebookInfo
page.category.primaryCategory
event[n].eventInfo.timeStamp
user.profile.social.twitter
transaction.item[n].linkedProduct[n].productInfo
Transaction (Billing Address)
page.category.subCategory1
event[n].eventInfo.effect
user.profile.social.twitterInfo
transaction.profile.address.line1
page.category.pageType
event[n].category.subCategory1
user.segment.isNewRegistration
transaction.profile.address.line2
page.attributes.numberOfComments
Version & Environment
event[n].attributes.nonInteractive
user.segment.isUserNewCustomer
transaction.profile.address.city
user.segment.isLoggedIn
transaction.profile.address.stateProvince
user.segment.customerClassDimension
transaction.profile.address.country
version: (1.0)
Security & Privacy
cat1.cat2.cat3.security.variableName
pageInstanceID:(dev | staging | live) privacy.accessCategories[n].domains
65
user.segment.visitorLifetimeValue
transaction.item[n].productInfo.productID
transaction.item[n].productInfo.description
Downloadable list digitalData layer object names in HTML & Excel
 Download this list as Excel here (or as HTML here).
 The PDF spec is here.
 A beta W3C toolkit website by Qubit is here.
66
Map W3C names to GTM names - used to pass validation
Example script - product items and component widgets require array mapping.
<script>
if(!window.digtialData) {digitalData = [];}
digtialData.push({
version: "1.0.1",
pageInstanceID: dataLayer.environment_Identifier, // e.g "production"
// Adwords Dynamic Variables for cart
cart.price.cartTotal: dataLayer.ecomm_totalvalue, // e.g 125
product.productInfo.productID: dataLayer.ecomm_prodid, // e.g "rog3000"
product.productInfo.productName: dataLayer.ecomm_pagetype, // e.g "basket"
product.productInfo.manufacturer: dataLayer.ecomm_pname, // e.g "nikon"
product.category.primaryCategory: dataLayer.ecomm_pcat, // e.g "cameras"
cart.attributes.isProductBundle: dataLayer.ecomm_rec_prodid, // e.g "yes"
page.pageInfo.pageID: dataLayer.page_pageID, // e.g "p1234"
page.pageInfo.pageName: dataLayer.page_title, // e.g "rogaine hair regrowth treatment"
page.pageInfo.sysEnv: dataLayer.page_attributes_SysEnv, // e.g "mobile"
page.pageInfo.destinationURL: dataLayer.page_virtual, // e.g "mysite.com/index.html"
page.pageInfo.referringURL: dataLayer.page_referral, // e.g "www.google.com/url?q=&esrc=s"
page.pageInfo.onsiteSearchTerm: dataLayer.page_siteSearchTerm, // e.g "keyword"
page.pageInfo.onsiteSearchResult : dataLayer.page_siteSearchResults, // e.g 10
page.pageInfo.language: dataLayer.settings_ga_locale, // e.g "en-us"
page.pageinfo.country: dataLayer.settings_ga_country, // e.g "us"
page.pageInfo.geoRegion: dataLayer.settings_ga_region, // e.g "us"
page.pageInfo.issueDate: dataLayer.page_contentCreated, // e.g "2013-09-01"
page.pageInfo.effectiveDate: dataLayer.page_contentModified, // e.g "2013-09-20"
page.attributes.numberOfComments: dataLayer.page_forumPosts, // e.g "5"
page.category.primaryCategory: dataLayer.page_group1_category, // e.g "faq pages"
page.category.subCategory1: dataLayer.page_group2_subCategory, // e.g "productinfo"
page.category.pageType: dataLayer.page_group3_templateName, // e.g "faq"
page.pageInfo.industryCodes: dataLayer.page_group4_funnelStep, // e.g "3990"
page.pageInfo.author: dataLayer.page_group5_author, // e.g "j smith"
transaction.transactionID: dataLayer.transactionId, // e.g "999999"
transaction.total.voucherCode: dataLayer.transactionAffiliation, // e.g "alpha"
transaction.total.currency: dataLayer.transaction_currency, // e.g "EUR"
transaction.total.taxRate: dataLayer.transactionTax, // e.g 0.20
transaction.total.shipping: dataLayer.transactionShipping, // e.g 5
transaction.total.shippingMethod: dataLayer.transaction_shippingMethod, // e.g "ups"
transaction.total.transactionTotal: dataLayer.transactionTotal, // e.g 125
transaction.attributes.paymentMethod: dataLayer.transaction_paymentMethod, // e.g "credit card"
transaction.item[0].productInfo.productID: dataLayer.transactionProducts_sku, // e.g "123"
transaction.item[0].productInfo.productName: dataLayer.transactionProducts_name, // e.g "acme blue
shoes"
transaction.item[0].category: dataLayer.transactionProducts_category, // e.g "shoes"
transaction.item[0].price: dataLayer.transactionProducts_price, // e.g 100
transaction.item[0].quantity: dataLayer.transactionProducts_quantity, // e.g 1
transaction.profile.address.city: dataLayer.transaction_city, // e.g "austin"
transaction.profile.address.stateProvince: dataLayer.transaction_region, // e.g "tx"
transaction.profile.address.country: dataLayer.transaction_country, // e.g "usa"
user.profile.profileInfo.profileID: dataLayer.user_cd005_userScope_userID, // e.g "user12345"
user.segment.isNewRegistration: dataLayer.user_cd001_userScope_isNewRegistration, // e.g "true"
user.segment.isUserNewCustomer: dataLayer.user_cd002_userScope_isUserNewCustomer, // e.g "true"
user.segment.isLoggedIn: dataLayer.user_cd003_userScope_isLoggedIn, // e.g "true"
user.segment.customerClassDimension: dataLayer.user_cd004_userScope_class, // e.g "platinum
customer"
user.segment.visitorLifetimeValue: dataLayer.user_cm001_currencyType_visitorLifetimeValue, // e.g
100
event.eventInfo.eventName: dataLayer.event, // e.g "add news portal"
event.category.primaryCategory: dataLayer.event_category, // e.g "portal"
event.eventInfo.eventAction: dataLayer.event_action, // e.g "addportal"
event.eventInfo.type: dataLayer.event_label, // e.g "contentmodifier"
event.eventInfo.eventPoints: dataLayer.event_value, // e.g 200
event.attributes.nonInteractive: dataLayer.event_nonInteractive, // e.g false
component[0].componentInfo.componentID: dataLayer.event, // e.g "rog300v"
component[0].category.primaryCategory: dataLayer.event_category, // e.g "haircare"
component[0].category.componentType: dataLayer.event_action, // e.g "flash movie"
component[0].componentInfo.description: dataLayer.event_label, // e.g "hair treatment video"
component[0].attributes.eventPoints: dataLayer.event_value,
component[0].attributes.nonInteractive: dataLayer.event_nonInteractive // e.g false
});</script>
67
W3C digitalData.privacy meta data & serverResponseForDNT
Sourced from customer experience digitalData Privacy sub-group on
JavaScript objects with Privacy meta data
digitalData.privacy.mapping = [
"page" : "public", // describes the page itself
"product" : "public", // further describes the product associated with the page
"cart" : "identifiable",
"transaction" : "sensitive",
"transaction total" : "private @analytics", // defined as private, but we make an
exception for our analytics tools
"event" : "public",
"privacy" : "public", // everyone should be able to see the general privacy definitions
and policy
"privacy mapping" : "@system", // in this example, we decide we don't want to expose the
exact policy mapping
"user" : "identifiable",
"user segment" : "public"
];
Privacy by Design: Client-side values - Phil proposed digitalData layer privacy object
for VISITOR
digitalData = {
"visitor": {
"returningStatus": "new", // new or returning visitor: used to only trigger consent
message for new visitors
"preferenceForDNT": window.navigator.doNotTrack, // yes|no|"not specified". MUST
defaulted to "not specified"
"anonymizeIp": false, // hash last 3 characters of IP address in GA. Defaulted to
off/false.
"geoplugin_status": geoplugin_status, // 403 error, 200 is look-up ok
"geoIPcountryCode": geoplugin_countryCode, // geo-plugin JS variable
"geoIPcontinentCode": geoplugin_continentCode // geo-plugin JS variable
},
{// Server-side USER values on login or registration
"user": {
"profile": {
"auth_isSignedIn": true,
"auth_isNewRegistration": true, // used to only trigger consent message on
first registration
"auth_userIDtoSessionIDoveride": false,
"profileID": 12345
}
}
}
}
Privacy by Design: Server-response to DNT from DNT Preference Expression Spec.
Value=obeyDNT | ignoresDNT | inprogressDNT | "not specified"
http://www.w3.org/TR/tracking-dnt/#status-representation
http://www.w3.org/2011/tracking-protection/drafts/tracking-dnt.html#status-representation
{
"targeting": "yes", // IsOnlineBehaviouralTargeting for Publishers OR onsite remarketing for
Advertisers enabled?
"tracking": "yes", // Is AudienceMeasurementTracking enabled
"qualifiers": "afc", // external "A"udit + "F"raud prevention + ad-frequency "C"apping
"controller": "http://www.clientdomain.com/privacy.html",
"same-party": [{
"google-analytics.com",
"stats.g.doubleclick.net",
"api.youtube.com"
}],
"third-party": [{
"googleadservices.com"
"ads.doubleclick.net",
}],
"audit": [{
"http://policy.cookiereports.com/caf4f823-en-gb.html" // e.g. w3.org/P3P/validator.html
}],
"policy": "/privacy.html#cookies",
"edit": "http://www.clientdomain.com/user-dashboard/edit-your-data"}
68