marketing channels cycle

Marketing Channels in JavaScript

All Web Analytics vendors provide out-of-the-box configurations to setup Marketing Channels and attribution reporting. However there will always be a case that you need to implement custom marketing channel detection logic in JavaScript through the tag management system. Even though it is unlikely the result will be identical to the one vendors provide, you need to get as close as possible.

I would like to highlight a few points that can help you get as accurate data as possible. Imagine the following scenario:

The marketing team wants to feed to their tools/systems with the marketing channel information customers are clicking through. This can be based either on tracking codes, referring domains or just be a direct visit. This information needs to be evaluated client-side using JavaScript. For simplicity, let’s assume that we do not need to care about “remembering” the last touch channel (attribution period); the marketing tool takes care of that. We just need to pass the information on a click-through basis.

What is your process?

Know your requirements

There is no saying how many times we make assumptions about our stakeholders’ needs. Make none! Keep in mind that Marketing Channels relate to marketing activities which translates to budget spent (£££). You need to provide the highest quality of work in terms of accuracy, robustness and maintainability; that includes knowing your end-goal. A few examples of simple requirements:

  • Marketing Channel is set to “XYZ” depending on the value of the tracking code (cmp) prefix
  • Marketing Channel Detail is set to the campaign code (cmp) of the respective channel (i.e. read the tracking code value)

However we can add some more complexity.

  • Distinguish Natural Search and Paid Search
  • Distinguish Natural Social and Paid Social
  • Capture referring domains but disregard known internal domains (i.e. payment gateways)
  • All channels should be checked and captured through-out the visit
  • Set as Direct only if it occurs at the beginning of the visit
  • The list of social, referring and internal domains is dynamic

This includes basic requirements and assumes that some processing takes place server side. However it is good starting point to start thinking how to proceed. Let’s visualise the high level requirements

marketing channels hierarchy
Channels Hierarchy

Write robust and simple solutions

Will try to break it down to some key points that most of the time affect accuracy and I have seen them repeat over-time:

Browser Support

Browsers are changing constantly, so does the JavaScript engine. It is expected from us to write modern code that takes advantage of the most recent features. let and const keywords, arrow functions etc. Until you realise things are not always peachy! In the chart below (taken from statcounter.com), IE11 has a significant market share in UK desktop traffic (7%). If your target audience has similar behaviour, you might want to think twice before using fancy features. Otherwise you will end up throwing exceptions and breaking the data collection process. True story! 🙂

statscounter browser market share
Browser market share – UK – Desktop

In general it is highly advisable to check browser support for any recently released feature before committing to it. Great source of such information is https://developer.mozilla.org/en-US/.

Know when a click-through occurs

You will need a generic way to identify when a click-through for any for the channels takes place. You can abstract the logic and cater for all channels. Let’s call it isChannelClickthrough(campaignCode, referrer). The campaign code (cmp, utm’s etc.) and the page referrer (document.referrer) are the all the information you will need (almost!).

The above implements the following logic checks in a waterfall fashion:

  1. If a channel parameter (campaign code – ?cmp=<…>) is set, then a paid channel is present
  2. Else if the referral is in place, check if the referring domain:
    1. matches known search engine domains
    2. matches know social media domains
    3. does NOT match known internal domains
  3. Else if no referral is present, check if its the first hit of the visit (aka Direct visit).

Note the order of identifyChannels() calls! Referring domains are at the end. This is important to first check for Natural Search and Natural Social. The reason is that first we are checking for a positive match while on the referring domains we are checking for the absence of a positive match across multiple domains (see below). If the above returns TRUE, you can then start populating and/or trigger marketing vendors that except this data. Let’s elaborate on how identifyChannel() works.

First point is to have a list of major domains that your audience goes through. This includes Search Engines, Social Media and known “friendly” domains. The last one are usually domains your customers interact with but are considered internal for the purposes of marketing attribution. Most likely this will include payment services, sites that span across multiple domains (www.my-site.com & www.my-site.co.uk).

For natural search and natural social, the referring domain needs to match any of the ones in the existing list. So the use of channel_domain_mapping.some() and .indexOf() do the job. However if we are checking for the referring domains, we need it to NOT match any of them. In this instance channel_domain_mapping.every() is used.

Where does Tealium fit in?

Since I have implemented the above in Tealium, had to use specific features which can be easily re-created in other Tag Management Systems:

  1. Within extensions and conditions, a data layer variable named “a” is available. It has two possible values; view and link depending on the type of event triggered (through utag.view or utag.link). It is suggested to trigger the whole marketing channel evaluation logic only when a page view is triggered. It’s not necessary you will have issues but being explicit and strict always helps in debugging data accuracy issues. So within extensions use the check a===’view’.
  2. b.tealium_session_event_number parameter is incremented every time a page view (utag.view) or link tracking (utag.link) is triggered within the session. It is an out of the box data layer variable that helps.

Maintainability & robustness in mind

One recurring issue I have faced is URL query string parameters’ names capitalisation. Depending on which person or application is creating the tracking code templates, you mind end up with multiple combinations. Some times people manually type the tracking parameters, in others different campaigns have different tracking templates. The same parameter (i.e. campaign code) might be configured as cmp, Cmp, CMP etc. The list goes on. A simple solution is to lower-case your keys only.

// ES6 fancy version with arrow function
var sanitised_search = window.location.search.replace(
/([^&?\/=]*)=/gi , x => x.toLowerCase()
);                  

This converts https://analyticsmayhem.com/?Cmp=CAMPAIGN_1 into ?cmp=CAMPAIGN_1. Note that the value of the parameter remains unaffected.

// ES5-compatible version
var sanitised_search = window.location.search.replace(
/([^&?\/=]*)=/gi , function(x) {return x.toLowerCase()}
);

Working with URL query string parameters can be tedious some times. One of the easiest way to manipulate them is using the URLSearchParams api. The main constructor expects a string in the format of “?key1=val1&key2=val2”. This can be easily extracted from window.location.search or manually create a URL object and read the search property.

// Create a URL object from a string
var url = new URL("http://example.com/search?query=lorem");
// Extract the URL query string parameters
var searchParams = new URLSearchParams(url.search);

searchParams.has("query") // returns true
searchParams.get("query") // returns "lorem"

The only downside is browser support; no support in IE. So this goes back to my first point; pay attention to your audience browser details!

Extensive QA

As expected, we always to QA complex/critical solutions. This is one of them. The main reason is not complexity but rather the important of data that it generates. You do not want to build something and 3 months down the line to figure out there was a deficiency with a particular channel (i.e. Affiliates) and you ended up paying incorrect commissions. Play it safe and test it! Test across multiple browsers, tracking code combinations, channel combinations, even across different campaigns within each channel.

Furthermore, include your stakeholders in this process. You want them to QA the data, sense-check them and make sure they have a say. An extra pair of eyes always helps! On top of that, they usually have much better knowledge of the metrics. So if something is off, they are in a much better position to spot it.

One point that I did not touch in this post is client-side persistence of the marketing channels (especially cross-domain). This requires a whole piece of analysis by itself! Maybe in a future post!

Enjoy!