Tag Archives: examples

Deploying Google Marketing Tags Asyncronously through DTM

I had posted previously about how to deploy marketing tags asynchronously through DTM, but Google Remarketing tags add an extra consideration: Google actually has a separate script to use if you want to deploy asynchronously. The idea is, you could reference the overall async script at the top of your page, then at any point later on, you would fire google_trackConversion to send your pixel. However, this is done slightly differently when you need your reference to that async script file to happen in the same code block as your pixel… you have to make sure the script has had a chance to load before you fire that trackConversion method, or you’ll get an error that “google_trackConversion is undefined”.

Below is an example of how I’ve done that in DTM.

//first, get the async google script, and make sure it has loaded
var dtmGOOGLE = document.createElement('SCRIPT');
var done = false;

dtmGOOGLE.setAttribute('src', '//www.googleadservices.com/pagead/conversion_async.js');
dtmGOOGLE.setAttribute('type','text/javascript');

document.body.appendChild(dtmGOOGLE);
dtmGOOGLE.onload = dtmGOOGLE.onreadystatechange = function () {
 if(!done && (!this.readyState || this.readyState === "loaded" || this.readyState === "complete")) {
 done = true;
 callback();

 // Handle memory leak in IE
 dtmGOOGLE.onload = dtmGOOGLE.onreadystatechange = null;
 document.body.removeChild(dtmGOOGLE);
 }
};

//then, create that pixel
function callback(){
 if(done){ 
 /* <![CDATA[ */
 window.google_trackConversion({
 google_conversion_id : 12345789,
 google_custom_params : window.google_tag_params,
 google_remarketing_only : true
 });
 //]]> 
 }
}

What the DTM “top down” approach means for your page performance

Any javascript framework, including all Tag Management Systems like DTM, have the potential to ADD more javascript weight to your page. But if you approach things the right way, this javascript weight  and its effect on your page performance can by mitigated by using DTM to optimize how your tools and tags are delivered. In a partner post, I’ll be talking about how to get the most out of DTM as far as Third Party Tags go, but I think one key concept is worth discussing explicitly.
You may have heard DTM be referred to as a “Top Down” TMS. For instance, this appears in some of the marketing slide decks:
topdown

While yes, it’s worth discussing this as a holistic approach to your digital analytics, it actually has a very real effect on how you set your rules up and how that affects page performance. That’s what I hope to discuss in this post.

In a different TMS, or even in DTM if I haven’t changed my mindset yet, I may be tempted to do something like this:

down-upRules

Where I have differing rules for different scopes as well as for different tags (we’re pretending here that “Wuggly” is a Third Party Marketing Pixel vendor).

DTM does what it can to defer code or make it non-blocking, but there are parts of the DTM library which will run as syncronous code on all pages. Some of that is because of the way the code needs to work- the Marketing Cloud ID service must run before the other Adobe tools; older Target mbox code versions need to run syncronously at top of page. But there is also the code in the library that serves as a map for when and how all of the deferred code should run. For instance, my library may include the following:

downUpCode

All of this logic exists to say “if the current pageType is “home page”, run this code non-sequentially”.  The name, conditions and event code for each rule run on each page as part of the overall library- these serve as a map for DTM to know which code must run, and which code it can ignore and not run.
You’ll notice the code for the two rules is completely identical, except for the rule name (in blue) and the source of the external script (in yellow). Most importantly, the conditions (in green) are identical. Whereas if they shared a rule, we might see the exact same thing as above accomplished with half as much code:

topDownCode

I now have ONE rule, which would be used for ALL logic that should run on the Home Page. The part of the library that runs on every page to check against conditions only has to check if the “pageType” is “home page” once, rather than twice. And DTM still loads the two scripts as separate non-sequential javascript. This doesn’t look like a major change, but when viewed across a whole implementation, where there may be dozens of rules, it can make a big difference in how many rules and conditions DTM must check on every page.

In the DTM interface, this would look like this:
topDownRules

If I want to know which rules contain my “Wuggly” codes, I can use the  “Tag Name” filter in the rules list, which will show me all rules that have a third party tag that includes “Wuggly”:
topDownFilter

This is filtering based on the Tag Name specified when you add the tag code:
topDownTagName

Using this approach, where your rules are based on their scope (or condition) and contain all logic- Analytics, Target, third party- that applies to that scope can not only lighten your library weight, but it can also keep your DTM implementation organized and scalable, but it may also require a change of mindset for how DTM is organized- if someone else needed to deploy a tag on Product Detail Pages, they wouldn’t need to create a rule, but rather, they could see a “Product Detail Page” rule already exists with the scope they need, and they need only add the third party tag.

There is one potential downside to consider, though- the approval and publication flow is based on Rules in their entirety. You can’t say “publish my changes to the analytics portion of this tool, but not to the third party tag section”. Still, if you are aware of this as you plan the publication of new features, this potential drawback rarely overrides the advantages.

How to approach Product Finding Methods

Product Finding Method (PFM) is a valuable custom report used by online retailers to tie Cart Adds and eventual Revenue to how users found the product. Unfortunately, these reports are often poorly configured or misunderstood. It is one of the best use cases for using the Merchandising setting on an eVar, but Merchandising, as a concept, is one of the more complicated topics in implementation.

Primer on Merchandising

In order to tie that PFM value to an individual product, and not the overall visit or visitor, we need to use Merchandising. And since we may not know the eventual product at the time that we know the finding method (for instance, if a user is using internal search, at the time we know they are searching, we don’t know what product they’ll eventually land on), we need to use Conversion Syntax- meaning we set it like a typical eVar (instead of in the products string) but have to configure at what point that eVar should be tied (or “bound”) to a product. Take the following user experience into consideration, where eVar5 is used for Product Finding Methods:

PFMflow

In the above, if I did NOT use Merchandising, my report might look like this, where External Campaign gets credit for everything up until it gets overwritten by Internal Search– at which point Internal Search gets credit for everything, including the entire Purchase event.

PFMnoMerchReportIf I DO use Merchandising, the External Campaign gets credit for whatever happens to the product it was bound to- in this case, the Blue Wug. Then Internal Search gets bound to the Red Wug, and gets credit for whatever happens to the Red Wug product:

PFMwMerchReport

We have to be very careful with the binding events (the event that tells Adobe to take whatever value we currently have floating around for that eVar and stick it to the current product), though- merchandising eVars won’t get credit for anything that happens if they are not bound to any product, or if that product isn’t currently present. For instance, in the example above, if you pulled in Internal Searches as a metric in that report, you’d see no PFMs were getting credit for the Internal Search event- even though eVar5 is set on the same page as our search event. That’s because that “Internal Search” value for eVar5 is waiting until a “binding event” to attach it to a product before it will get credit for any metrics, and the “External Campaign” value for eVar5 only gets visibility into things that happen to the product it’s bound to (Blue Wug). No products on the Internal Search page means Conversion-Syntax Merchandising eVars get no credit for the Search event.

How to Plan Your Product Finding Methods

To design your PFM solution, start by thinking of the different ways a client can add an item to their cart. We’ll call these “add to cart location” and give that its own eVar. Typical scenarios are:

  • product details page
  • quick view
  • from wishlist
  • from order history
  • recommendation/cross sell modules

Next, figure out all the ways a user could get to any of those above scenarios. These are Product Finding Methods and should get their own eVar. Typical PFMs include:

  • internal search
  • external site (referrerss and natural search)
  • external campaigns (that take the user directly to the PDP)
  • internal promotion (eg, homepage hero banner)
  • browse (naturally navigating through the menu or breadcrumbs)
  • cross-sell
  • recommendations
  • wishlist
  • order history 

You’ll notice some of your cart add locations may also be product finding methods- that’s ok. Note that Products Detail Page is NOT a PFM- the Product Details page is specific to the product, which is the thing being found not the thing doing the finding.

Both of these eVars will need to be set up for Merchandising (this is set in the Admin console>Report Suites>Edit Settings>Conversion>Conversion Variables). For the Add to Cart Location eVar, since we know the product at the time we know the add to cart location, you can use “product syntax”.

eVar4PFM

This is preferable because it leaves little room for error and has the easiest configuration. Let’s say I’ve set eVar4 to be my “Add to Cart location” eVar. At the time my user adds any item to their cart, I would set eVar4 (often, you can just set it to the current page’s page type):

s.products=";sku1;;;;eVar4=PDP"
s.events="scAdd"

But for Product Finding Method, you often don’t know what product the value for our eVar will eventually bind to, so we need to set it to conversion syntax, and we need to carefully consider which events should bind the current PFM value to the current product. Generally, Product View (ideally a custom event, not prodView) and Cart Add suffice as binding events.

eVar5pfm

DO NOT set to bind on ALL events, as many events happen on your site where your PFM eVar doesn’t have a desirable value.

Next, to implement, we need to figure out where to set each of our PFM values. If you’re using a tag management system like DTM, you can often just piggy back on existing rules for many of your finding methods, but others may need to live in global logic (like in your s_code). See below for a possible configuration.

internal search Set on internal search results page
external site Set in global logic- if no other PFM is present, and the user came from a domain other than your own
external campaigns Set in global logic, if s.campaign is set
internal promotion Set in global logic, if internal campaign eVar is set (or internal campaign query string parameter is present).
browse Set on all Product List Pages and Category pages
cross-sell Set when user clicks or clicks through on a cross-sell module
recommendations Set when user clicks or clicks through on a recommendation module
wishlist Set on Wish List page
order history Set on Order History page

Some folks manage PFM entirely in their s_code or in their (global DTM tool settings) based on whether other variables have been set (“if internal search term eVar has a value, set eVar5 to ‘internal search”‘). For instance, one client has something similar to this, in addition to setting their PFM eVar directly on certain pages (like their List Pages and Category pages, or certain microsites):

//easy way to get just the hostname of the referring site
   var a=document.createElement('a');
   a.href=document.referrer;
   s.refDomain=a.hostname;

if(!s.eVar5){ //if PFM not currently set on the page
   if(s.campaign){s.eVar5="campaign"} 
   else if(s.eVar10){s.eVar5="internal search"}
   else if(s.eVar23){s.eVar5="recommended product"} 
   else if(s.pageName=="My Account>Wishlist"){s.eVar5="my wishlist"}
   else if(document.referrer==""){s.eVar5="direct or bookmarked"}
   else if(s.refDomain.indexOf("mydomain.com")==-1){s.eVar5="external site"} 
 }
   else if(s.eVar15){s.eVar26="internal campaign"} 
}

Conclusion

Hopefully, this gives some ideas and examples for how to get valuable reporting on how users are finding and purchasing products on your site. I’d love to hear about potential scenarios or solutions I’ve missed!

How do I use DTM for a Single Page App?

The question of how to use DTM on Single Page Apps (SPAs) is a VERY hot item right now. By Single Page App, I’m referring to a full user flow contained on a single web page, so as to provide the user a more seamless experience. Often, these pages act like typical web pages, but they don’t always change URLs or load new resources. Many common web development technologies, such as Angular.js, Ember.js, and AJAX use SPA principles.

Unfortunately, there isn’t a single great answer for how to deploy DTM- it depends on many things. I’ll work through some of the options and the limitations to be aware of.

Suppressing Page View Beacons

Whatever method you take for tracking page views in a SPA, keep in mind most SPAs do have one true “page view” when the DOM first loads. If you are going strictly with the DCR or EBR route, you may need to suppress the initial page view beacon the Analytics tool will want to set by default. Otherwise, in the example below where the developers are firing a direct call rule on all page views, you’d get TWO beacons on the first page and 1 on all subsequent pages.

Picture1

Data Layer Considerations

You’ll need to make sure that whatever the sources of your Data Elements are (CSS selector, javascript objects, cookies…) have the correct values BEFORE your rule is triggered. I have an example site showing one way you might do this for a data layer (though you’ll need to look in the source code), but ultimately it’s going to depend on your site.

Variable Persistence

One last consideration is that once your Analytics object exists (as in, the “s” in “s.pageName”), variables set on it will continue to exist unless specifically overwritten. In most cases, you’d overwrite s.pageName with a new value so it isn’t a problem, but something like s.eVar5 may be set on the first beacon in your SPA, and not desired on subsequent beacons. You can use s.clearVars() to “refresh” your “s” object, but you have to make sure it fires at the right time- for example, after the beacon on Page A, but before DTM starts mapping Data Elements to variables for the beacon on Page B. How you do this will depend on the overall deployment method you choose.

Deployment Methods

1) Direct Call Rules

Perhaps the most straight-forward approach is to have developers fire a Direct Call Rule, like _satellite.track(“page view”) on every thing YOU consider a page view, whether it’s a fresh new DOM or not.

Advantages: Disadvantages: 
  • You have ultimate control over when a page view in considered a page view.
  • If you need to clear out variables between beacons (for instance, you set s.eVar5 in the first beacon in the SPA, and don’t want it in the second beacon), Direct Call Rules don’t provide a great place to use something like s.clearsVars(). There are some potential work-arounds, but none are ideal.
  • Developers need to add more DTM-specific code (satellite.track) to your pages.
  • Direct Call Rules don’t allow for extra conditions (like “fire THIS logic on pageA, and THAT logic on pageB”) in the interface.
  • Direct Call Rules don’t “stack”- if multiple rules have conditions that are met, multiple rules will fire.

2) pushState or hashChange

Many SPA frameworks, like Angular, use a certain flag to let the browser know the user is viewing a new “page”.

pushState

DTM can listen for this flag in an Event Based Rule using a pushState or hashChange condition.

ADVANTAGES: DISADVANTAGES: 
  • No additional code is needed- most SPA frameworks are already firing something DTM can listen to
  • It’s an Event Based Rule, which allows you to fire clearVars(), and set extra conditions
  • Because you are listening for an event set by the framework, you have less control over timing. Updating a data layer BEFORE the “pushState” event is detected would be critical.
  • Event Based Rules don’t “stack”- if multiple rules have conditions that are met, multiple rules will fire.

3) Custom Event EBR

Another option, which feels a bit like a blend of the first two options, is to use a Custom Event-based Event Based Rule (and no, that’s not a typo- it’s an EBR based on the JavaScript Concept of a Custom Event). It’s possible Developers are already using this Custom Event concept for their own code and purposes, and DTM can just listen for it… or you can have developers set one specific to our DTM needs by using something like my digitalData.userAction hack.

ADVANTAGES: DISADVANTAGES: 
  • You have a little more control over timing
  • It’s an Event Based Rule, which allows you to fire clearVars(), and set extra conditions
  • May require more developer work- similar level of effort as DCRs
  • Event Based Rules don’t “stack”- if multiple rules have conditions that are met, multiple rules will fire.

 4) (NEW OPTION!) “dataelementchanged” Event Based Rule

Just in the last few weeks, a new option emerged as part of the AEM Context Hub integration. This allows DTM to listen for changes to certain data elements- for instance, you could have a rule fire whenever the “pageName” has changed. My friends at 33 sticks have a great blog post about this already for more info.

ADVANTAGES: DISADVANTAGES: 
  • You have a little more control over timing
  • It’s an Event Based Rule, which allows you to fire clearVars(), and set extra conditions
  • Requires careful consideration of when the data layer changes/loads
  • Event Based Rules don’t “stack”- if multiple rules have conditions that are met, multiple rules will fire.

Setting up an Event-based Rule that be fired directly like a Direct Call Rule

Update:
As proud as I am of this solution/workaround, it may be far less needed now that you can use the dataelementchanged  condition to fire an Event Based Rule. Instead of using custom events, you can just have DTM listen for when your pageName Data Element has changed. 

The current limitations

If developers want to fire a DTM rule directly from their code (say, they want to make sure a beacon fires only after their data layer is ready), typically they would fire a Direct Call Rule, with its very specific syntax: _satellite.track(“rule string here”). There are, however, some limitations to this method.

Direct Call Rules:

  • Don’t allow for multiple conditions (you can’t say “if _satellite.track(“cart add”) is fired AND the current page has “/products” in the URL“)
  • Don’t allow for multiple arguments (you can’t pass _satellite.track(“cart add”,”sku1″) to attach the added SKU to the rule)
  • Don’t allow for firing s.clearVars() before your rule sets up your analytics variables (to clear out variables from previous beacons on the same DOM).
  • Require very specific syntax- they MUST be “_satellite.track()”

And unfortunately, both Direct Call Rules and Event-based rules don’t “stack”- if a certain condition triggers multiple similar rules, each rule will fire its own beacon. This is different from Page Load Rules, where if multiple rules have conditions being met by the current page, they all wrap nicely into a single page view beacon.

An alternative

To get around some (but maybe not all) of these limitations, I’ve been playing with another possible option, where we use the Custom Event conditions of an Event Based Rule to accomplish nearly the same thing. After getting it set up, I can fire something like this:

digitalData.userAction("cart add","sku1")

…to fire an Event-Based Rule in place of a Direct Call Rule. There are a few things I need to do in DTM to make this digitalData.userAction work.

Set Up the Logic

First, I have to set up the logic in a Page Load Rule- set to fire on DOMReady (no need for it to be sooner)- that will merely hold the following as a Sequential Javascript Third Party Tag:

//make sure digitalData is defined to prevent errors
if(typeof digitalData=="undefined"){
  digitalData={}
}

//create fake DOM item to bind the event to
var fakeDiv = document.createElement('div');
fakeDiv.setAttribute('id', "dtmHolder");
fakeDiv.setAttribute('height','1');
fakeDiv.setAttribute('width','1');
fakeDiv.setAttribute('style','display:none');
document.body.appendChild(fakeDiv);

//define custom event
digitalData.userAction=function(n,details){

 document.getElementById("dtmHolder").addEventListener("dtmEvent", function(e) {
    console.info("Event is: ", e);
  })

  // First create the event
  var dtmEvent = new CustomEvent(n, {
    detail:""
  });

  jQuery("#dtmHolder").attr("detail",details)
  
  // Trigger it!
  document.getElementById("dtmHolder").dispatchEvent(dtmEvent); 
}

(Update: note that this code should not run before the DOM is created- it will create an error if you try to run it at page top because you are trying to append something to a body that doesn’t exist yet).
Now, whenever a developer fires digitalData.userAction(“string here), you can listen for that string as the Triggered Event Type in a Custom Event Event Based Rule. Obviously, you can alter the above code if you want a function named something other than digitalData.userAction.

Set Up an Event Based Rule

The rule will need to be bound to the CSs selector of the tiny fake div (“#dtmHolder”) we created for the custom event to bind to:

2016-04-20_12-38-12

You can create as many of these rules as you want, for whatever different strings you pass into digitalData.userAction()where the “triggered event type” reflects that string.

Pass Additional Info

If you want to pass a second argument ( e.g. digitalData.userAction(“cart add”,”sku1″)) I currently have that second argument  passing as a new attribute (“detail”) on the tiny invisible div, so you can access it off the “this” object directly in the rule:2016-04-20_12-41-52

You can give this a try at my ugly test site– open a developer console, turn on DTM debugging, and fire either digitalData.userAction(“cartAdd”,”sku123″) or digitalData.userAction(“pageView”) to see two example rules at work.

Run ClearVars

This opens the ability to run s.clearVars on s.t() beacons in cases where multiple beacons may be firing on a single DOM. (As a reminder, if you’re using the old DCR route, there are some hack-ish options for doing this- we call it daisy-chaining).

In an Event Based Rule, there IS a code block that runs before the Analytics tool, giving you a perfect opportunity to make sure you are starting with a ‘clean slate’ of variables: the Conditions Custom Code block. Just create a new Rule Condition with “custom” criteria, then put “s.clearVars()” in the code block, followed by “return true” (so that DTM doesn’t think some condition didn’t pass):

2016-04-20_12-52-49

You can also apply additional conditions, like “only fire this “cart add” rule on certain pages”, by adding more criteria under Rule Conditions.

Conclusion

I’m very open to suggestions and feedback on this- maybe we can crowdsource a better way, but for now, this seems to be a reasonable alternative to Direct Call Rules. Let me know what you think!