Quick Tip: Delayed Tracking with DTM

Sometimes you find yourself in a situation where you need to track something outside the normal way.

Examples can be when you want to track data that is not available directly on page load, such as product availability.

Easy: use DTM

In theory, this should be easy: Just make a Page Load Rule, set “Trigger Rule at” to “Onload” under Conditions, and Bob’s your uncle.

[Screenshot]
Trigger Rule at Onload
The great thing: you can then administer all tracking you want to do via Data Elements and the fields in the “Adobe Analytics” section of the rule.

But alas, sometimes this is not good enough. Sometimes you need to wait a bit longer, specifically if the site you are working with uses asynchronous loading of data.

There is an added bit of complexity with situations like these. You have to make sure there are no “race conditions”.

A race condition is when two pieces of code execute independently and do not always do so in the order you need.

The standard tracking on your page could be slow for whatever reason, and your delayed tracking could happen earlier. That could pretty much mess up tracking.

So what do we do?

Yes: use DTM

The answer is still to use DTM, but let’s make sure we deal with the race conditions. We will have to do three things:

  1. Wait for the “normal” tracking on the page to fire
  2. delay a bit more
  3. track our data

The third thing is easy, we have done it before many times. But 1 and 2 deserve some more attention. Luckily for us, Javascript comes with two handy methods: setInterval() and setTimeout().

While setInterval() allows us to execute code repeatedly (like checking for something, maybe?), setTimeout() is good for executing code after a bit of time, once.

One question left: how do we know the tracking request has fired?

You can check for the existence of an object named “s_i_<rsid>”, which gets attached to window when the tracking call goes out. That is probably the easiest way.

So, without further ado, here is the complete code:

[Screenshot]
Delayed Tracking Script
Put all of this into a sequential or non-sequential Javascript block in a new Page Load Rule (which you can trigger at “Top of Page”, if you want!)

Some of that needs a bit of explaining, I guess.

In lines 1 – 4, I load the necessary constants from Data Elements.

The rsid is an obvious one, and I’ll use “Check Interval” as the interval length for the setInterval() call, and “Delay Time” as wait time for setTimeout();

I have set “Check Interval” to 100 and “Delay Time” to 1000.

Meaning: the code will check every 100 milliseconds (10 times per second) whether tracking has gone out, then wait 100 milliseconds (1 second).

I used Data Elements for these constants because I’ll potentially be having multiple of these rules and I want to be able to change the times centrally.

Lines 9 & 10 check for the existence of that “s_i<rsid>” element.

Lines 12 – 17 cancel the interval timer plus the watch dog (more later).

Lines 20 – 25 are where your tracking code goes. You can set props, eVars and events here, then you must call s.t() or s.tl().

Don’t forget s.linkTrackVars & s.linkTrackEvents if you use s.tl()!

In lines 30 – 33 I define a second timeout. It waits 10s then calls a function that kills the interval. I don’t want that check to run on forever, and the assumption is that if after 10s no tracking has happened, it probably never will.

You could argue that this should happen much earlier than after 10 seconds, and I’d say you’d be right.

I hope this’ll help someone, and let me know if you can see any improvements!

13 thoughts on “Quick Tip: Delayed Tracking with DTM

  1. Isn’t it easier to just use setTimeout with an if-check instead setInterval with double settimeout? if the check is true setTimeout will execute and end, else it will check again after the amount of time.

    Like

  2. What i meant is something like:

    (function doCheck(){
    setTimeout(function(){
    if (typeof …) {
    /* Code goes in here */
    }else{
    doCheck();
    }
    }, 5);
    })();

    Like

  3. What i meant is something like:

    (function doCheck(){
    setTimeout(function(){
    if (typeof …) {
    /* Code goes in here */
    }else{
    doCheck();
    }
    }, 5);
    })();

    Like

    1. Hi Till,

      Yup, that works as well.

      I find setInterval has one single advantage: if the loop gets executed _a lot_, setTimeout called recursively might hit some maximum recursion limit in the browser, while setInterval will not.

      Like

  4. If you are not using the Visitor ID service, then I would also recommend waiting for ireq.complete == true. If this is the first visit of the visitor, you also want to make sure that the s_vi cookie has been set.

    Like

  5. It helps me a lot because I recently had several cases where I decided to implement something similar (not in DTM which I don’t like that much honestly, but in Tealium and Google Tag Manager). Your post showed me that we didn’t do anything out of the ordinary with these intervals.

    In one of the cases, we needed to wait until jQuery had really been loaded on the page because we wanted to deploy some jQuery-reliant functionality in a JavaScript that came through the Tag Management System.
    jQuery was not loaded through the TMS but through the website (as it should be) on DOM Ready, but our script should fire on DOM Ready as well, so it was never assured that jQuery had already been initialized – and in fact, especially in Firefox, jQuery often wouldn’t be ready yet when our TMS script kicked in (which of course resulted in an “undefined” error).

    So just like you are checking again and again for that s_i object, I would check for the jQuery object. Once jQuery was detected, the interval was killed and the script executed. The interval was also killed after 30 unsuccessful tries (it checked every 0.5 seconds, so we could safely assume that if jQuery was not there yet after 15 seconds, there must be other more serious problems on that page.)

    Like

  6. Hi Jan,

    Excellent post, as always. It’s full of useful and rather timely advice.

    Two follow up questions came to mind…

    Q1. Would the RSID comma (‘,’) delimiter need to be replaced with underscore (‘_’) when setting intID – just in case a site has multiple RSID’s?

    var rsid = _satellite.getVar(“ReportSuiteID”).replace(“,”, “_”);
    var intID = window[“s_i_” + rsid]

    Q2. Is there a way to check if specific props/evar/etc are populated (already), before dynamically setting s.linkTrackVars and s.linkTrackEvents?

    *Appreciate this might be a limited use case, but any advice would be gratefully received.

    Thanks,
    Alex

    Like

    1. Hi Alex,

      You’re right with the underscore, yes.

      For the second question, I guess other than going through a loop for each, I can’t think of a way.

      HTH,
      Jan

      Like

  7. Thanks for this, Jan!

    I have a call that is firing too early, before some content on the page finishes loading, specifically the H1 tag. How would I set this up to check to see if the H1 tag has been populated before setting the variables?

    Like

    1. Hi Chris,

      That is one of those “depends” questions, I’m afraid.

      I have seen people do it with delayed tracking, essentially waiting a little bit (say 2 seconds), then tracking the page, (hopefully) including that H1 tag.

      Other people have tracked the page, then sent a second request.

      Others have cancelled the page load tracking and used Event-based Rules (“elementexists” works, or “datalayerchange”), yet others do it with Direct-call Rules.

      It is mainly about how stable the delay is.

      And the best fix, of course, is to make the H1 appear earlier, which would be a job for your dev department.

      Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.