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