Adobe Dynamic Tag Management. It' interesting concept. If you are a SiteCatalyst user, this system is great! It's free, you can add in other tags, and it has a pretty good function set overall that can compete with other tag managers. I might compare different tag managers in the future, but for now, I'd like to cover a piece of DTM that is missing, but can be very useful.

This piece is custom tracking calls. What I mean by this is being able to fire an event with data at any point you'd like. DTM provides your typical page load tracking, and built in event tracking. However, this event tracking isn't perfect--especially for something like single page apps. Luckily, since Adobe just bought the older "Satellite Tag Management", they inherited their tracking code. Maybe I can illustrate the problem and solution with a sample use case.

Say we have an application like Google Plus. There are many functions on this site, all of which load dynamically depending on what you click on. There's no guarantee that each post or item will have a unique id or class (or similar) that we can use the DTM event selectors to track with. Let's say, in this case, we want to do a tracking call every time a profile photo is clicked on a post. Now, these may or may not have a class to key off of and we know that these posts load very dynamically. So, they may be difficult to track.

Given this use case, we could implement raw tracking codes. SiteCatalyst has their s.t() and functions. Google Analytics has their _gaq.push(). There are similar calls for many other cases. But, since we're using DTM, we don't want to do a raw tracking function call, especially if we want to fire multiple vendors from one tracking call. Some tag managers have some very visible functions to handle this. For example, Google Tag Manager (GTM) has dataLayer.push({params}) and Tealium has{params}) or utag.view({params}). DTM also has something similar from the Satellite system called _satellite.track('<event name>').

Now, if you've played around in DTM much, you may have wondered what the direct call rules are. The function above is directly tied to those rules. Basically, the <event name> is a string that you'd want to use to trigger a direct call rule with the same string in DTM. You can call this in any situation to call these rules, so it meets the need of our use case. When we click a profile photo, we can call the _satellite.track function to fire our vendor calls.

If you compare the examples of the DTM, GTM, and Tealium calls above, you'll see that DTM might be lacking something. GTM and Tealium both allow ways to pass the tag management system different variables with values for your tracking call. We can achieve the same affect with DTM by defining a variable (preferably global) with our variables. Maybe like so:

 window.my_data = { my_var: 'my value' };
window.my_data = {};

In this example, you will set up each variable in DTM to look for a javascript my_data.variable_name. Then, you'd also have a direct call rule in which you'll have your mapped variables into a tag.

Now, tracking like this isn't always ideal since you have to have a bit of extra overhead to think about with every tracking call. For this, I've created an extra function you can use instead called dtm_track. Basically, you'll add a function like so anytime you'd like to track an action:

 dtm_track('my-event',{ my_var: 'my_value' });

This keeps it clean to one function call as well as the internal code uses a standard variable name for tracking. You'll setup each data element looking for dtm_layer.my_var. To keep it standard, your page load data layer would also be setup with the same object name (dtm_layer) so you only have to have one set of variables at any point.

The only extra piece that needs to be configured in DTM to get this working will be to:

  1. Create a page load tag.
  2. Name it "Add Tracking Function"
  3. In the javascript section, add the code found at the bottom of this post.
  4. Check the box that says to "execute globally".

I've glazed over a few details above as I have been assuming that you already have some experience with DTM. If additional questions arise, you can comment below and I'll try my best to answer it!


Per the steps above, you'll need the tracking function. Here's the code that I use (as of 11/6/14) as well as some documentation on how to use it:

/* function dtm_track
 *   @param type -- Required. The type of call. Defaults to "view".
 *   @param obj -- Optional. An object of data elements for mapping to a tag.
 *   @description This function is used for tracking some event dynamically.
 *     This is used in conjunction with a special process in direct call
 *     rules and js object data elements.
 *     -- The <obj> param --
 *     All data elements passed into the <obj> param of this function should
 *     be created in dtm as js objects named "dtm_layer.<element_name>".
 *     Each possible item in the object should be created as a data element
 *     and then mapped in the associated direct call rule.
 *     -- The <type> param --
 *     All direct call rules will have the string in the format of <type>.
 *     So, you might have "view". In SiteCatalyst, for example, you would
 *     put this string and then init an s.t() call because it is a "view".
 *     -- Debugging -- 
 *     If you'd like to view the output of these calls as they happen, enable
 *     the debug cookie in the console with the following:
 *         document.cookie="dtm_debug=true";
 *     -- Additional Info --
 *     This code also will initialize a page with an empty dtm_layer object if
 *     one isn't defined already. In addition, a global cloning function is
 *     created called "dtm_clone". It will clone the following js types: string,
 *     object, boolean, array, date, number, function. It will NOT copy anything
 *     that has prototype methods defined. It will also not copy anything that
 *     is cylic or recursive in nature. If done, a stack overflow error will
 *     result. This method can be called on any variable like so:
 *         myClonedVar = dtm_clone(myOldVar);

window.dtm_clone = function(obj) {
  if (null == obj || "object" != typeof obj)
    return obj;

  if (obj instanceof Date) {
    var copy = new Date();
    return copy;

  if (obj instanceof Array) {
    var copy = [];
    for (var i = 0, len = obj.length; i < len; i++) {
      copy[i] = dtm_clone(obj[i]);
    return copy;

  if (obj instanceof Object) {
    var copy = {};
    for (var attr in obj) {
      if (obj.hasOwnProperty(attr)) copy[attr] = dtm_clone(obj[attr]);
    return copy;

  if (typeof console != "undefined" && console.log)
    console.log('clone: type "'+(typeof obj)+'" not supported')
window.dtm_layer = window.dtm_layer || {};
window.dtm_track = function(type,obj) {
  try {
    var hasTracked = "";
    if (typeof _satellite == "undefined") hasTracked = "NOT ";
    if (typeof type == "undefined" || type == null || !type) type = "view";
    var temp = window.dtm_clone(window.dtm_layer);
    window.dtm_layer = obj || {};
    if (typeof console != "undefined" && console.log
        && typeof _satellite.readCookie('dtm_debug') != "undefined"
        && (_satellite.readCookie('dtm_debug') == "true"
            || _satellite.readCookie('dtm_debug') == true)) {
      console.log("dtm " + hasTracked + "tracked '" + type + "'", obj);
    if (hasTracked != "NOT ")
    window.dtm_layer = window.dtm_clone(temp);
    delete temp;
  } catch (e) {
    if(typeof console != "undefined" && console.log)
        console.log("dtm_track error. Message: ",e.getMessage()," Passed obj: ",obj);
  return true;