Event-based funnel reports in Google Analytics

Dmytro Bulakh
5 min readJan 17, 2019

--

Update July 2023. Please note that this information is severely outdated since Google killed Universal Analytics :(

Funnels can be quite complex: they may consist of several events on different pages which can be reached independently. In such case you can’t rely on standard tools anymore.

Consider the following flow:

Your visitor starts with a three section quiz on a landing page, then an email prompt pops up to deliver detailed results of the quiz, after that the visitor is directed to a recommendation page based on their quiz answers, where finally the visitor picks recommended items and proceeds to the checkout. Whenever the quiz, recommendation and checkout pages exist as separate pages, you can set them as a funnel step using the funnel visualization Google Analytics tool. But if interactions as email prompt or recommendations display happen on a single page you won’t be able to integrate quiz steps progress into the funnel visualization unless you’ve into sending virtual pageviews. Additionally, funnel visualization report has other drawbacks and weakness. Alternative solution is to switch to an event based funnel tracking. One can send specific events hits to Google Analytics at each stage of the funnel, like ‘quiz start’, ‘email prompt’, ‘checkout’. This approach, however, may fail when, for example, the checkout stage is reached independently of the funnel flow.

Here I’ll describe a different approach to dealing with this issue. My solution uses session level cookie to keep track of the form progress and allows you to get a nice-looking funnel progress report:

First, we need to choose funnel stage names. A value from the list must be passed to GTM to inform the tag manager that the user has reached a specific step. In our example we will consider four basic stages and put their names into Custom JS Variable in order to have them as a JS array called stageNames :

Then we’ll need a Custom HTML tag that will track the funnel progress and store a current funnel stage identifier in the cookie. But how will we trigger the script? The most convenient way is to bind the ‘cookie processing’ tag to some custom events. One can easily emit custom events with a small custom HTML tag bound to any desired interaction, such as page view, clicks or form submits, etc.

So within the scope of the current example, the suggested flow is as follows:

  1. emit a custom event with the name from the stageNames array.
  2. trigger the ‘cookie processing’ Custom HTML tag that checks if the event follows the preceding stage and if yes,
  3. compose the ‘funnel stage string’
  4. update the cookie
  5. pass a composed string to Google Analytics as an event action.

Let’s take a closer look at how this happens:

Let’s emit event #1 quiz started and then email prompt:

...
dataLayer.push({'event' : 'quiz started'})
...
//later after the user completes the quiz and is prompted for an email to receive the results
...
dataLayer.push({'event' : 'email prompt'})
...

Both quiz started and email prompt events trigger our ‘cookie processing’ custom HTML tag. With the first run, this tag records the cookie value to quiz started. With the second run this tag records the cookie value to quiz started > email prompt. Finally, the cookie processing tag pushes the event to dataLayer, indicating that the processed stage name is available as the cookie value. This triggers the GA tag with an event action field set to cookie value variable.

Such trick finally results in nice report lines:

Let’s take a closer look under the hood of the Custom HTML ‘cookie processing’ Tag. This is fired by our pushes to dataLayer and utilizes the following variables:

{{stageNames}} — an array of stages
{{Event}} — the name of dataLayer event

This piece of the JavaScript gets the event, checks it against an array of stages, composes the string that represents the stage and saves it to gtm_funnel_stage cookie and then pushes the ‘funnel_stage’ event to dataLayer:

<script>
//Helper function to get cookie value
function gtmGetCookie(cname) {
var name = cname + "=";
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);
if (c.indexOf(name) != -1) return c.substring(name.length, c.length);
}
return null;
}
//Helper function to set cookie value
function gtmSetCookie(cname, value) {
document.cookie = "mtracker=somevalue; path=/".replace('somevalue', value).replace('mtracker', cname);
}
//Helper function to remove cookie
function gtmRemoveCookie(cname) {
document.cookie = "mtracker=somevalue; path=/; expires=Thu, 01 Jan 1970 00:00:01 GMT;".replace('mtracker', cname);
}
// Processing of funnel progress
function setStage(eventName) {
var stageNames = {{stageNames}}
var delimiter = ' > '
var strict = false
var currentStage = gtmGetCookie('gtm_funnel_stage') && gtmGetCookie('gtm_funnel_stage').split(delimiter).pop()

// Start a new flow if the first event form the funnel registered
if (stageNames.indexOf(eventName) == 0) {
gtmSetCookie('gtm_funnel_stage', eventName)
// If there is a next stage registered update the cookie
} else if (currentStage && stageNames.indexOf(eventName) > stageNames.indexOf(currentStage) ) {
gtmSetCookie('gtm_funnel_stage', gtmGetCookie('gtm_funnel_stage') + delimiter + eventName)
} else {
// Optionally: reset the reporting if the flow is broken
if (strict) gtmRemoveCookie('gtm_funnel_stage')
return
}
dataLayer.push({'event': 'funnel_stage'})
}
setStage({{Event}})
</script>

This is triggered by any event from the stageNames list.

An actual GA hit is sent by a GA event tag, triggered by the ‘funnel_stage’ custom event with {{current stage}} 1st party cookie variable.

We will also need some custom tags that indicate our stages, such as the email prompt tag triggered by email form visibility:

Finally, now you may access your funnel figures by looking at unique events at the Events Report in Google Analytics. This can also be easily visualized in Data Studio.

Tweaking this to your needs simply requires setting up your own stage names variable and setting up tags to push them properly to dataLayer.

--

--

Dmytro Bulakh

Marketing professional, Google Analytics and Google AdWords certified specialist at ppchead.com, beer lover and football aficionado