About The Author

James Rosewell has 18 years experience in the mobile industry. He led many initiatives for Vodafone, including the development of the 1st mobile web application …

More about James

Enabling Multiscreen Tracking With Google Analytics

Quick Summary

We are increasingly using responsive design, responsive design with server-side components (RESS), adaptive design and combinations thereof to provide great high-performance multiscreen experiences. However, analytics implementations often miss information that is important to understanding how a website is being used on different devices. For example, a website that varies the navigation layout based on screen size or user preferences might provide different user flows through the website depending on the layout being used. By enhancing Google Analytics, you’ll be able to identify and optimize under-performing layouts and screen sizes to improve performance on any device.

Table of Contents
Membership counter

Members support Smashing

Wonderful, friendly people who keep this lil' site alive — and get smarter every day.

Are you smashing, too? →

We are increasingly using responsive design, responsive design with server-side components (RESS), adaptive design and combinations thereof to provide great high-performance multiscreen experiences. However, analytics implementations often miss information that is important to understanding how a website is being used on different devices.

For example, a website that varies the navigation layout based on screen size or user preferences might provide different user flows through the website depending on the layout being used. By enhancing Google Analytics, you’ll be able to identify and optimize under-performing layouts and screen sizes to improve performance on any device.

Further Reading on SmashingMag:

This article outlines common challenges and how to configure Google’s new Universal Analytics to efficiently overcome them, using features such as custom dimensions, enhanced link tracking and server-side data feeds. Information about the user’s layout preferences, the screen’s orientation, which link (of multiple) was used to arrive at a destination, and the physical size of the device’s screen can all be easily added. Universal Analytics enables data to be fed more efficiently from the web server, thus improving performance.

Common Challenges

Screen Size

Google Analytics categorizes devices as desktop, mobile or tablet. Information that is important to designers, such as physical screen size, is lost by such segmentation. Consider a web page that performs well on a modern 4.7-inch smartphone (such as the iPhone 6). Suppose a key message appears in the bottom quarter of the screen when displayed on such a device in portrait orientation. The same page on a 3.5-inch smartphone (such as the iPhone 4) would not show this key message without being scrolled down. The designer might not have been aware that this particular message is key to the success of the page. Different device-orientation preferences complicate the challenge further. Analytics tools provide the insight needed to drive continual understanding and improvement.

Information about device types and screen sizes is lost in the default configuration.
Information about device types and screen sizes is lost in the default configuration. (Image credit: 51Degrees) (View large version)

Screen sizes also vary significantly by country. This difference can have a profound effect on a website’s success. The following chart shows average screen sizes of mobile devices in square inches by country, as measured from real web traffic.

Average mobile device screen size in square inches for South Africa, India and the UK by month
Average mobile device screen size in square inches for South Africa, India and the UK by month. (Image credit: 51Degrees) (View large version)

Layout

Layout is different from physical screen size, although layout is often driven by screen information measured in pixels and device orientation. Understanding the layout being used when analyzing website activity is essential to continual design improvement.

First, consider advertising. Experience shows that a single persistent advert across the top of the web page that remains permanently in view will perform well on a smartphone. On a tablet, skyscraper and banner adverts might work more effectively because a persistent advert is considered annoying.

A commercially minded website owner would want their analytics solution to be able to tell how different ads perform based on the layout and screen size, so that they can identify over- or under-performing configurations and increase their advertising revenue.

The main content is even more important. Information about the active layout for the page — whether determined by media queries or via the user’s preference for a desktop-, smartphone- or tablet-optimized layout — can improve understanding. Consider a page with five primary action rectangles, which are laid out horizontally on a desktop but appear vertically on a smartphone in portrait orientation. The fifth rectangle at the bottom of the list would be viewed less frequently on smartphones. Understanding the impact of this difference in position and viewing probability could lead you to experiment with different positioning for smartphones.

Performance

Modern websites tend to put greater demand on the client device’s processor. Google Analytics is an example of a tool with small overhead that does not directly enhance the user experience. Additional processing capacity and network resources are needed to execute its functionality, consuming precious battery life. With Universal Analytics, data can now be fed from the server, rather than via JavaScript running in the web browser.

Bring On Universal Analytics

In late 2013, Universal Analytics left beta, and it is now available for all websites. Google now recommends upgrading to Universal Analytics from Classic. Universal Analytics has several advantages:

  1. The JavaScript snippet and include are more sensitive, improving the quality of the information obtained.
  2. Interacting with and customizing the snippet is easier.
  3. More custom dimensions and metrics are available in the free version.
  4. Options other than JavaScript are available to feed activity information, including from the web server and from Android and iOS applications.

Upgrading to Universal from Classic is a two-step process. First, upgrade the web property via Google Analytics’ administration website. If the property is not already using Universal, then an option will appear under the “Admin” menu advising of the option. However, Google is so keen to upgrade websites that it might have already applied the change for you! If so, then the “Tracking code” page will start with, “This is the Universal Analytics tracking code for this property.”

The screen that confirms a web property’s use of Universal Analytics
The screen that confirms a web property’s use of Universal Analytics. (View large version)

The second part you’ll have to do yourself: changing the JavaScript snippet on your website. The JavaScript snippet must not be upgraded before the property has been changed to Universal Analytics in the previous step. The simplest implementation is this:

<script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', '[TRACKING_ID]', 'auto'); ga('send', 'pageview'); </script>

You would replace [TRACKING_ID] with the unique tracking ID code for your web property.

Custom Dimensions

Once the upgrade to Universal is complete, you can add information to Google Analytics via “Custom Dimensions.”

Custom dimensions can be defined under the “Property” menu on the “Administration” screen. The following screenshot shows a property without any dimensions configured.

Empty list of custom dimensions
Empty list of custom dimensions. (View large version)

Up to 20 dimensions may be added to a property in the free version of Google Analytics, which is 15 more than in the Classic version. The paid version enables 200.

Device Orientation

Device orientation can easily be calculated in JavaScript by determining the height and width of the window. If the height is larger than the width, then the device is in portrait orientation. If not, then it’s in landscape orientation. The following JavaScript returns an orientation string using this logic:

window.innerHeight > window.innerWidth ? 'Portrait' : 'Landscape'

By creating a custom dimension and feeding its value from this JavaScript, we can easily add this information about the page request to Google Analytics. The following screenshot shows the simple dialog that is displayed when the “New Custom Dimension” button is pressed. Because the orientation of the device can change during a session, the new custom dimension is added for it, with a scope of “Hit.”

A custom dimension for orientation with a scope of Hit
A custom dimension for orientation with a scope of “Hit”.

The “Orientation” dimension has been added, with an index starting at “1.” The following screenshot shows the new dimension and its index. At the time of writing, I’m unaware of a way to remove custom dimensions in the free version of Google Analytics, so plan ahead!

Initial list of one custom dimension
Initial list of one custom dimension. (View large version)

Feeding Custom Dimensions

Now that the dimension has been created for analysis, we need to populate it with data. This entails adding the JavaScript that returns the required data to Google Analytics’ data feed.

Setting an index of the dimension on the “Administration” screen that is the same as the dimension used in the code that sends data to Google Analytics is critical. In this case, the index is “1” and the key field, therefore, is dimension1. The following Google Analytics snippet has been enhanced to add the custom dimension data at an index of 1.

<script>

(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga');  
ga('create', '[TRACKING_ID]', 'auto'); 
ga('set', 'dimension1', window.innerHeight > window.innerWidth ? 'Portrait' : 'Landscape');
ga('send', 'pageview');

</script>

New Field

A new custom dimension, named “Orientation,” will now be available for analysis. It will appear in the list of available fields under the main heading “Custom Dimensions,” and it can be accessed from segment conditions, as a secondary dimension, and from any other areas of the Google Analytics reporting interface that lists fields.

Custom dimensions included in fields list
Custom dimensions included in fields list.

Let It Settle

Patience is a virtue, and time is needed to validate the results of changes. The following screenshot shows the percentage of users by orientation over a one-month period.

Sample analysis of custom dimension orientation
Sample analysis of custom dimension orientation.

Screen Size

Third-party data sources can also be used to feed custom dimensions. 51Degrees (the company I founded) can detect information about a screen’s physical size, returning the number of square inches or the diagonal length, rounded to the nearest integer value.

Once enabled, segments can be created based on physical screen size to compare different outcomes, answering questions such as:

  • Does the bounce rate vary by physical screen size for particular pages?
  • How is goal completion affected by physical screen size?

Unlike the orientation dimension, physical screen size does not change during a session, so it is added with the “Session” scope.

Adding a custom dimension to a web property with a scope of Session
Adding a custom dimension to a web property with a scope of “Session”.

The “ScreenInchesDiagonalRounded” dimension has been added, with an index of “2.”

Now that the dimension has been created for analysis, we need to populate it with data. To make the examples in this article easy to follow, I’ve created a website that is configured to detect devices and provide the necessary JavaScript resources. The following snippet includes a resource that provides access to the “ScreenInchesDiagonalRounded” property.

<script src="http://js.51degrees.com/51Degrees.features.js?ScreenInchesDiagonalRounded" type="text/javascript"></script>

With the snippet above added before the Google Analytics snippet, the “ScreenInchesDiagonalRounded” dimension (with an index of “2” in the list of custom dimensions) can be populated with the value returned by the “Fifty One Degrees features” include, exposed via the FODF object.

<script>

(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', '[TRACKING_ID]', 'auto'); 
ga('set', 'dimension1', window.innerHeight > window.innerWidth ? 'Portrait' : 'Landscape'); 
ga('set', 'dimension2', FODF.ScreenInchesDiagonalRounded); 
ga('send', 'pageview');

</script>

The highlighted additional line of code sets the second dimension to a value of FODF.ScreenInchesDiagonalRounded, which is the screen’s diagonal length in inches, rounded to the nearest integer.

Insight

The following screenshot shows the percentage of users by physical screen size, measured in inches and over a one-month period.

Sample data of custom dimension for screen diagonal
Sample data of custom dimension for screen diagonal. (View large version)

We can now clearly identify usage by 3-, 4-, 5- and 6-inch screens within what Google would classify as the “mobile” device type. We can also see that 10-inch screens dominate the “tablet” device type.

Performance Is King

Measurement should have no impact on the activity being observed. Website analysis performed client-side, as shown earlier, does have a small impact on performance, because the relevant JavaScript needs to be downloaded, parsed and then executed. In both cases,a request is sent to Google Analytics to provide the information associated with the hit or event. While this overhead isn’t noticeable on a fast high-bandwidth data connection, minimize it as much as possible for mobile.

The network resource inspector in modern web browsers such as Chrome clearly shows the problem. The following screenshot shows the 51Degrees.features.js request from the earlier example. We see that 76 milliseconds are wasted while the browser waits to send the request for the resource.

Example of JavaScript being blocked
Example of JavaScript being blocked.

Fortunately, this problem can be eliminated easily in several ways.

Asynchronous JavaScript

One approach is to load both the analytics.js file and the features script asynchronously. The onload event of the features script can be used to load the Google Analytics script and set the custom dimensions needed by Google. The following code snippet would replace those provided earlier, including the 51Degrees.features.js include.

<script type="text/javascript">

(function(d,t,s,a,p){ a = d.createElement(t); a.type = 'text/javascript'; a.async = true; a.src = s; p = d.getElementsByTagName(t)[0]; a.onload = function() { (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', '[TRACKING_ID]', 'auto'); 
ga('set', 'dimension1', window.innerHeight > window.innerWidth ? 'Portrait' : 'Landscape'); 
ga('set', 'dimension2', FODF.ScreenInchesDiagonalRounded); 
ga('send', 'pageview'); }; 
p.parentNode.insertBefore(a,p); })(document,'script','http://js.51degrees.com/51Degrees.features.js?ScreenInchesDiagonalRounded');

</script>

Using the network inspector, we can see the difference in the screenshot below.

Example of JavaScript loading asynchronously
Example of JavaScript loading asynchronously.

For websites that use downstream caching to reduce the load on the server, this technique is a very efficient way to provide client-specific information to the browser.

Server-Side

The only way to eliminate the impact of processing and sending information from the client to Google Analytics is to perform this activity on the server. This technique only works when the web server can see all of the traffic and when the web pages are not publicly cached (although the content they contain could still be publicly cached or served from a content delivery network). As such, the technique cannot be used on all websites or all pages.

The Google Analytics code snippet makes a GET request that contains usage and tracking information in the query string. The network inspector reveals the output of Google Analytics’ JavaScript. We see a filter for requests to https://www.google-analytics.com/collect and a request similar to the following:

https://www.google-analytics.com/collect?v=1&_v=j23&a=123456789&t=pageview&_s=1&dl=https%3A%2F%2Fexample.com%2FPage&ul=en-us&de=UTF-8&dt=Page&sd=24-bit&sr=1280x720&vp=1263x622&je=1&fl=14.0%20r0&_u=eDCAgEQi~&jid=&cid=123456789.123456789&tid=UA-123456789-1&cd1=6&z=459486931

Universal Analytics no longer requires that this information be sent from the web browser. It may be sent from anywhere, including the web server or a native application. Google refer to sending data in this way as the “measurement protocol.” Sending usage data from the server has the immediate benefit of reducing the amount of processing and the number of web connections taken up by the browser.

At least two HTTP requests are eliminated for each page request because Google’s analytics.js script does not need to load and the resulting request to /collect is also avoided. You could argue that another benefit is that the user can no longer avoid being tracked by turning off requests sent to Google Analytics. Of course, consider the non-technical implications of this, too — namely, as it relates to your terms and conditions and your privacy policy.

The following C# snippet could be added to a .NET web page to send most information to Google Analytics.

var parameters = new Dictionary<string, string>();  

// Add the basic header parameters. 
parameters.Add("v", "1"); 
parameters.Add("t", "pageview"); 
parameters.Add("tid", "INSERT TRACKING ID"); 
parameters.Add("z", Convert.ToBase64String(Guid.NewGuid().ToByteArray()));  

// Add client information. 
if (Request.Cookies[CLIENT_ID_COOKIE_NAME] != null) 
{ 
// Cookie already exists so use that one. 
parameters.Add("cid", 
Request.Cookies[CLIENT_ID_COOKIE_NAME].Value); 
} 
else 
{ 
// This is a new request so set the client ID.
var buffer = new List<byte>>();

// Use the IP address of the client as the first part of the CID. 
IPAddress clientIP; 
if (IPAddress.TryParse(Request.UserHostAddress, out clientIP)) buffer.AddRange(clientIP.GetAddressBytes()); 
else 
buffer.AddRange(Encoding.ASCII.GetBytes(Request.UserHostAddress));  

// Use the date as the second part. 
buffer.AddRange(BitConverter.GetBytes((short)DateTime.UtcNow.Year)); 
buffer.AddRange(BitConverter.GetBytes((byte)DateTime.UtcNow.Month)); 
buffer.AddRange(BitConverter.GetBytes((byte)DateTime.UtcNow.Day)); 
buffer.AddRange(BitConverter.GetBytes((short)DateTime.UtcNow.TimeOfDay.TotalMinutes));  

// Use a hash code of the user agent for the final part. 
buffer.AddRange(BitConverter.GetBytes(Request.UserAgent.GetHashCode()));  

var clientIdCookie = new HttpCookie( 
CLIENT_ID_COOKIE_NAME, 
Convert.ToBase64String(buffer.ToArray()));  

clientIdCookie.HttpOnly = true; 
clientIdCookie.Expires = DateTime.UtcNow.AddYears(2); 
Response.Cookies.Add(clientIdCookie); 
parameters.Add("cid", clientIdCookie.Value); 
} 
parameters.Add("uip", Request.UserHostAddress);  

// If the session is new, indicate this to Google. if (Session != null && 
Session.IsNewSession) 
{ 
parameters.Add("sc", "start"); 
}  

// Add user agent and device information. 
parameters.Add("ua", Request.UserAgent); 
parameters.Add("je", Request.Browser.JavaScript ? "1" : "0"); 
if (Request.Browser.IsMobileDevice) 
{ 
var screenSize = String.Format( 
"{0}x{1}", 
Request.Browser.ScreenPixelsWidth, 
Request.Browser.ScreenPixelsHeight); 
parameters.Add("sr", screenSize); 
parameters.Add("vp", screenSize); 
}  

// Add language information. 
if (Request.UserLanguages != null && 
Request.UserLanguages.Length > 0) 
parameters.Add("ul", Request.UserLanguages[0]);  

// Add page information. 
Uri page; 
if (Uri.TryCreate(Request.Url, Request.RawUrl, out page) == false) 
page = Request.Url; 
parameters.Add("dp", page.AbsolutePath); 
if (String.IsNullOrEmpty(Page.Title) == false) 
parameters.Add("dt", Page.Title); 
parameters.Add("dh", page.Host); 
parameters.Add("dl", page.ToString()); 
parameters.Add("de", 
Response.ContentEncoding.WebName.ToUpperInvariant());  

// Add referrer information. 
if (Request.UrlReferrer != null) 
parameters.Add("dr", Request.UrlReferrer.ToString());  

// Add campaign parameters. 
if (Request.QueryString["utm_campaign"] != null) 
parameters.Add("cn", Request.QueryString["utm_campaign"]); 
if (Request.QueryString["utm_content"] != null) 
parameters.Add("cc", Request.QueryString["utm_content"]); 
if (Request.QueryString["utm_term"] != null) 
parameters.Add("ck", Request.QueryString["utm_term"]); 
if (Request.QueryString["utm_medium"] != null) 
parameters.Add("cm", Request.QueryString["utm_medium"]);
if (Request.QueryString["utm_source"] != null) 
parameters.Add("cs", Request.QueryString["utm_term"]);  

// Add user ID information, if known. 
if (Page.User != null && 
String.IsNullOrEmpty(Page.User.Identity.Name) == false) 
{ 
parameters.Add("uid", Page.User.Identity.Name); 
}  

// Finally, add our custom dimension! 
parameters.Add("dimension2", 
Request.Browser["ScreenInchesDiagonalRounded"]);  

// Build the URL for Google. 
var url = String.Concat( 
"https://ssl.google-analytics.com/collect?", 
String.Join("&", parameters.Select(p => 
String.Format("{0}={1}", 
p.Key, 
HttpUtility.UrlEncode(p.Value, Encoding.UTF8))).ToArray())); 

// Send the request asynchronously. 
var request = HttpWebRequest.Create(url); 
request.CachePolicy = new 
RequestCachePolicy(RequestCacheLevel.NoCacheNoStore); 
request.Method = "GET"; 
request.Timeout = 20000; 
request.BeginGetResponse(YourCallBackFunction, request);

That’s a lot of code because we need to do all of the things that the analytics.js snippet does client-side in JavaScript but on the server. At the time of writing, Google does not offer any official code for any platforms.

In the .NET snippet, a collection named parameters is populated with the key-value pairs that will form the query string (often called the “payload”) to be sent to Google Analytics. The following parameters are mandatory in every request:

  • v This is the protocol’s version and will always be 1.
  • tid This is the tracking ID, which is the same as the one used in the JavaScript earlier.
  • cid The client ID is used to anonymously identify a device, browser or user.
  • t This is the type of hit being recorded and will usually be a page view.

Also setting the dp parameter is a good idea because it identifies the page being viewed and is essential to any practical analysis. A full list of the measurement protocol’s parameters is available on Google.

The important line for adding custom dimensions is towards the end, where the dimension2 parameter is set. For this line to work, 51Degrees’ device-detection solution must be available on the server. In .NET, the simplest way to add the library is via NuGet. Using PHP device-detection, you can add 51Degrees’ library by unzipping the necessary files and including the scripts in the PHP page. Other languages are supported, including Java, Python, Perl and C.

Another modification to make is to check that the device that is requesting the page is not a crawler. Google Analytics does a reasonable job of removing crawler traffic, but we could help it by checking whether a device is a crawler, spider or bot and, if so, not sending any data.

So far, we’ve explored techniques to track page views. Another useful feature in Google Analytics is the capability to track precisely which link is used to navigate to a destination, to track elements that could have more than one destination (such as a search button) or to track actions that are managed in JavaScript. Google calls this feature “enhanced link attribution.”

Consider a web page that contains three links to the same destination: one in the main navigation menu, one as part of a featured section and the last in the page’s footer. Knowing which of these three links was used to navigate to a destination could help you to improve the design of the page. When combined with data on screen size and (if relevant) the current layout, this information could improve our understanding of how users navigate the website.

Enhanced link tracking can be easily enabled in Universal Analytics via the “Property Settings” menu in the “In-Page Analytics” section, where a form similar to the following screenshot appears:

Configuring enhanced link attribution
Configuring enhanced link attribution. (View large version)

Simply turn the “Use enhanced link attribution” feature on, and add the following JavaScript line after the create parameter, which includes the tracking ID:

ga('require', 'linkid', 'linkid.js');

Enhanced link attribution works best when every navigation element on the page has a unique ID attribute, like the one in the following line of code:

Screen Size

Third-party data sources can also be used to feed custom dimensions. 51Degrees (the company I founded) can detect information about a screen’s physical size, returning the number of square inches or the diagonal length, rounded to the nearest integer value.

Once enabled, segments can be created based on physical screen size to compare different outcomes, answering questions such as:

  • Does the bounce rate vary by physical screen size for particular pages?
  • How is goal completion affected by physical screen size?

Unlike the orientation dimension, physical screen size does not change during a session, so it is added with the “Session” scope.

Adding a custom dimension to a web property with a scope of Session
Adding a custom dimension to a web property with a scope of “Session”.

The “ScreenInchesDiagonalRounded” dimension has been added, with an index of “2.”

Now that the dimension has been created for analysis, we need to populate it with data. To make the examples in this article easy to follow, I’ve created a website that is configured to detect devices and provide the necessary JavaScript resources. The following snippet includes a resource that provides access to the “ScreenInchesDiagonalRounded” property.

<script src="http://js.51degrees.com/51Degrees.features.js?ScreenInchesDiagonalRounded" type="text/javascript"></script>

With the snippet above added before the Google Analytics snippet, the “ScreenInchesDiagonalRounded” dimension (with an index of “2” in the list of custom dimensions) can be populated with the value returned by the “Fifty One Degrees features” include, exposed via the FODF object.

<script>

(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', '[TRACKING_ID]', 'auto'); 
ga('set', 'dimension1', window.innerHeight > window.innerWidth ? 'Portrait' : 'Landscape'); 
ga('set', 'dimension2', FODF.ScreenInchesDiagonalRounded); 
ga('send', 'pageview');

</script>

The highlighted additional line of code sets the second dimension to a value of FODF.ScreenInchesDiagonalRounded, which is the screen’s diagonal length in inches, rounded to the nearest integer.

Insight

The following screenshot shows the percentage of users by physical screen size, measured in inches and over a one-month period.

Sample data of custom dimension for screen diagonal
Sample data of custom dimension for screen diagonal. (View large version)

We can now clearly identify usage by 3-, 4-, 5- and 6-inch screens within what Google would classify as the “mobile” device type. We can also see that 10-inch screens dominate the “tablet” device type.

Performance Is King

Measurement should have no impact on the activity being observed. Website analysis performed client-side, as shown earlier, does have a small impact on performance, because the relevant JavaScript needs to be downloaded, parsed and then executed. In both cases,a request is sent to Google Analytics to provide the information associated with the hit or event. While this overhead isn’t noticeable on a fast high-bandwidth data connection, minimize it as much as possible for mobile.

The network resource inspector in modern web browsers such as Chrome clearly shows the problem. The following screenshot shows the 51Degrees.features.js request from the earlier example. We see that 76 milliseconds are wasted while the browser waits to send the request for the resource.

Example of JavaScript being blocked
Example of JavaScript being blocked.

Fortunately, this problem can be eliminated easily in several ways.

Asynchronous JavaScript

One approach is to load both the analytics.js file and the features script asynchronously. The onload event of the features script can be used to load the Google Analytics script and set the custom dimensions needed by Google. The following code snippet would replace those provided earlier, including the 51Degrees.features.js include.

<script type="text/javascript">

(function(d,t,s,a,p){ a = d.createElement(t); a.type = 'text/javascript'; a.async = true; a.src = s; p = d.getElementsByTagName(t)[0]; a.onload = function() { (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', '[TRACKING_ID]', 'auto'); 
ga('set', 'dimension1', window.innerHeight > window.innerWidth ? 'Portrait' : 'Landscape'); 
ga('set', 'dimension2', FODF.ScreenInchesDiagonalRounded); 
ga('send', 'pageview'); }; 
p.parentNode.insertBefore(a,p); })(document,'script','http://js.51degrees.com/51Degrees.features.js?ScreenInchesDiagonalRounded');

</script>

Using the network inspector, we can see the difference in the screenshot below.

Example of JavaScript loading asynchronously
Example of JavaScript loading asynchronously.

For websites that use downstream caching to reduce the load on the server, this technique is a very efficient way to provide client-specific information to the browser.

Server-Side

The only way to eliminate the impact of processing and sending information from the client to Google Analytics is to perform this activity on the server. This technique only works when the web server can see all of the traffic and when the web pages are not publicly cached (although the content they contain could still be publicly cached or served from a content delivery network). As such, the technique cannot be used on all websites or all pages.

The Google Analytics code snippet makes a GET request that contains usage and tracking information in the query string. The network inspector reveals the output of Google Analytics’ JavaScript. We see a filter for requests to https://www.google-analytics.com/collect and a request similar to the following:

https://www.google-analytics.com/collect?v=1&_v=j23&a=123456789&t=pageview&_s=1&dl=https%3A%2F%2Fexample.com%2FPage&ul=en-us&de=UTF-8&dt=Page&sd=24-bit&sr=1280x720&vp=1263x622&je=1&fl=14.0%20r0&_u=eDCAgEQi~&jid=&cid=123456789.123456789&tid=UA-123456789-1&cd1=6&z=459486931

Universal Analytics no longer requires that this information be sent from the web browser. It may be sent from anywhere, including the web server or a native application. Google refer to sending data in this way as the “measurement protocol.” Sending usage data from the server has the immediate benefit of reducing the amount of processing and the number of web connections taken up by the browser.

At least two HTTP requests are eliminated for each page request because Google’s analytics.js script does not need to load and the resulting request to /collect is also avoided. You could argue that another benefit is that the user can no longer avoid being tracked by turning off requests sent to Google Analytics. Of course, consider the non-technical implications of this, too — namely, as it relates to your terms and conditions and your privacy policy.

The following C# snippet could be added to a .NET web page to send most information to Google Analytics.

var parameters = new Dictionary<string, string>();  

// Add the basic header parameters. 
parameters.Add("v", "1"); 
parameters.Add("t", "pageview"); 
parameters.Add("tid", "INSERT TRACKING ID"); 
parameters.Add("z", Convert.ToBase64String(Guid.NewGuid().ToByteArray()));  

// Add client information. 
if (Request.Cookies[CLIENT_ID_COOKIE_NAME] != null) 
{ 
// Cookie already exists so use that one. 
parameters.Add("cid", 
Request.Cookies[CLIENT_ID_COOKIE_NAME].Value); 
} 
else 
{ 
// This is a new request so set the client ID.
var buffer = new List<byte>>();

// Use the IP address of the client as the first part of the CID. 
IPAddress clientIP; 
if (IPAddress.TryParse(Request.UserHostAddress, out clientIP)) buffer.AddRange(clientIP.GetAddressBytes()); 
else 
buffer.AddRange(Encoding.ASCII.GetBytes(Request.UserHostAddress));  

// Use the date as the second part. 
buffer.AddRange(BitConverter.GetBytes((short)DateTime.UtcNow.Year)); 
buffer.AddRange(BitConverter.GetBytes((byte)DateTime.UtcNow.Month)); 
buffer.AddRange(BitConverter.GetBytes((byte)DateTime.UtcNow.Day)); 
buffer.AddRange(BitConverter.GetBytes((short)DateTime.UtcNow.TimeOfDay.TotalMinutes));  

// Use a hash code of the user agent for the final part. 
buffer.AddRange(BitConverter.GetBytes(Request.UserAgent.GetHashCode()));  

var clientIdCookie = new HttpCookie( 
CLIENT_ID_COOKIE_NAME, 
Convert.ToBase64String(buffer.ToArray()));  

clientIdCookie.HttpOnly = true; 
clientIdCookie.Expires = DateTime.UtcNow.AddYears(2); 
Response.Cookies.Add(clientIdCookie); 
parameters.Add("cid", clientIdCookie.Value); 
} 
parameters.Add("uip", Request.UserHostAddress);  

// If the session is new, indicate this to Google. if (Session != null && 
Session.IsNewSession) 
{ 
parameters.Add("sc", "start"); 
}  

// Add user agent and device information. 
parameters.Add("ua", Request.UserAgent); 
parameters.Add("je", Request.Browser.JavaScript ? "1" : "0"); 
if (Request.Browser.IsMobileDevice) 
{ 
var screenSize = String.Format( 
"{0}x{1}", 
Request.Browser.ScreenPixelsWidth, 
Request.Browser.ScreenPixelsHeight); 
parameters.Add("sr", screenSize); 
parameters.Add("vp", screenSize); 
}  

// Add language information. 
if (Request.UserLanguages != null && 
Request.UserLanguages.Length > 0) 
parameters.Add("ul", Request.UserLanguages[0]);  

// Add page information. 
Uri page; 
if (Uri.TryCreate(Request.Url, Request.RawUrl, out page) == false) 
page = Request.Url; 
parameters.Add("dp", page.AbsolutePath); 
if (String.IsNullOrEmpty(Page.Title) == false) 
parameters.Add("dt", Page.Title); 
parameters.Add("dh", page.Host); 
parameters.Add("dl", page.ToString()); 
parameters.Add("de", 
Response.ContentEncoding.WebName.ToUpperInvariant());  

// Add referrer information. 
if (Request.UrlReferrer != null) 
parameters.Add("dr", Request.UrlReferrer.ToString());  

// Add campaign parameters. 
if (Request.QueryString["utm_campaign"] != null) 
parameters.Add("cn", Request.QueryString["utm_campaign"]); 
if (Request.QueryString["utm_content"] != null) 
parameters.Add("cc", Request.QueryString["utm_content"]); 
if (Request.QueryString["utm_term"] != null) 
parameters.Add("ck", Request.QueryString["utm_term"]); 
if (Request.QueryString["utm_medium"] != null) 
parameters.Add("cm", Request.QueryString["utm_medium"]);
if (Request.QueryString["utm_source"] != null) 
parameters.Add("cs", Request.QueryString["utm_term"]);  

// Add user ID information, if known. 
if (Page.User != null && 
String.IsNullOrEmpty(Page.User.Identity.Name) == false) 
{ 
parameters.Add("uid", Page.User.Identity.Name); 
}  

// Finally, add our custom dimension! 
parameters.Add("dimension2", 
Request.Browser["ScreenInchesDiagonalRounded"]);  

// Build the URL for Google. 
var url = String.Concat( 
"https://ssl.google-analytics.com/collect?", 
String.Join("&", parameters.Select(p => 
String.Format("{0}={1}", 
p.Key, 
HttpUtility.UrlEncode(p.Value, Encoding.UTF8))).ToArray())); 

// Send the request asynchronously. 
var request = HttpWebRequest.Create(url); 
request.CachePolicy = new 
RequestCachePolicy(RequestCacheLevel.NoCacheNoStore); 
request.Method = "GET"; 
request.Timeout = 20000; 
request.BeginGetResponse(YourCallBackFunction, request);

That’s a lot of code because we need to do all of the things that the analytics.js snippet does client-side in JavaScript but on the server. At the time of writing, Google does not offer any official code for any platforms.

In the .NET snippet, a collection named parameters is populated with the key-value pairs that will form the query string (often called the “payload”) to be sent to Google Analytics. The following parameters are mandatory in every request:

  • v This is the protocol’s version and will always be 1.
  • tid This is the tracking ID, which is the same as the one used in the JavaScript earlier.
  • cid The client ID is used to anonymously identify a device, browser or user.
  • t This is the type of hit being recorded and will usually be a page view.

Also setting the dp parameter is a good idea because it identifies the page being viewed and is essential to any practical analysis. A full list of the measurement protocol’s parameters is available on Google.

The important line for adding custom dimensions is towards the end, where the dimension2 parameter is set. For this line to work, 51Degrees’ device-detection solution must be available on the server. In .NET, the simplest way to add the library is via NuGet. Using PHP device-detection, you can add 51Degrees’ library by unzipping the necessary files and including the scripts in the PHP page. Other languages are supported, including Java, Python, Perl and C.

Another modification to make is to check that the device that is requesting the page is not a crawler. Google Analytics does a reasonable job of removing crawler traffic, but we could help it by checking whether a device is a crawler, spider or bot and, if so, not sending any data.

So far, we’ve explored techniques to track page views. Another useful feature in Google Analytics is the capability to track precisely which link is used to navigate to a destination, to track elements that could have more than one destination (such as a search button) or to track actions that are managed in JavaScript. Google calls this feature “enhanced link attribution.”

Consider a web page that contains three links to the same destination: one in the main navigation menu, one as part of a featured section and the last in the page’s footer. Knowing which of these three links was used to navigate to a destination could help you to improve the design of the page. When combined with data on screen size and (if relevant) the current layout, this information could improve our understanding of how users navigate the website.

Enhanced link tracking can be easily enabled in Universal Analytics via the “Property Settings” menu in the “In-Page Analytics” section, where a form similar to the following screenshot appears:

Configuring enhanced link attribution
Configuring enhanced link attribution. (View large version)

Simply turn the “Use enhanced link attribution” feature on, and add the following JavaScript line after the create parameter, which includes the tracking ID:

ga('require', 'linkid', 'linkid.js');

Enhanced link attribution works best when every navigation element on the page has a unique ID attribute, like the one in the following line of code:

<a href='NewPage.html' id='NewPageLink1'>Goto another page</a>

If the ID attribute isn’t present, then Google Analytics will try to identify the element based on the position of the element in the DOM. However, this is not as reliable.

Multiple Views

Google’s analytics.js script automatically tracks the size of the screen and the active viewport via JavaScript detection. You can use this information to analyze how responsive web pages perform at different breakpoints without additional modification.

For websites that enhance responsive design with RESS or adaptive techniques (varying the page’s content or navigation based on the user’s preference for a particular view type), then this information should be provided to Google Analytics for subsequent analysis. When this preference is a part of the page’s URL structure (for example, prefixing the domain name with an m. for the mobile-optimized section), then the information will automatically be made available to Google Analytics.

Different Techniques

The main problem with URLs that vary based on characteristics such as device type is that they don’t work very well when shared. For example, someone on a phone who wants to tweet the URL of an interesting page might share the m. version of the URL. Followers who open the link would be presented with the m. variant of the page, which will be suboptimal if they’re on a tablet or desktop.

A far better technique is to vary the response of a URL according to the device type and the user’s preference for a particular view. All of Microsoft’s new web technologies now favour model-view-controller (MVC) design. RESS and adaptive design are particularly easy to implement in MVC because the view is designed to be altered based on knowledge of the client device or user preferences, thus improving performance and the user experience. For example, someone on a 4.5-inch phone might prefer to pinch and zoom on the desktop interface of a website if they’re more familiar with it or if it contains more information.

By default, Google Analytics does not provide this insight. It categorizes devices as desktop, mobile or tablet only. When a page’s layout changes, an enhancement is needed to provide information associated with the active view.

The following screenshots are of the same URL, featuring a table that compares device-detection options. The layouts for small-screen devices and for big-screen web browsers are obviously different.

The table's layout changes according to the user's selected view.
The table’s layout changes according to the user’s selected view. (Image credit: 51Degrees) (View large version)

If the user has not expressed a preference for a view type, then the URL will default to serving content that is optimized for the device type. The user may change the view at any time should they not be happy with the default. They might prefer the big-screen version and be content to pinch and zoom around the page. We need to track this information in Google Analytics because it provides important insight into the viewing preferences of users.

Custom Dimensions

Custom dimensions can be used to record this information. The following screen shows a “ViewType” custom dimension being added, with a scope of “Hit.”

Editing a custom dimension in Google Analytics with scope of Hit
Editing a custom dimension in Google Analytics with scope of “Hit”.

Assuming that “ViewType” is given an index of “3,” the following lines of JavaScript could be added to determine the active view based on the presence of different menu element IDs on the page.

var activeView = 'Desktop'; 
if (document.getElementById('mobileHeader') != undefined) { activeView = 'SmartPhone'; 
} else if (document.getElementById('tabletHeader') != undefined) { activeView = 'Tablet'; 
} 
ga('set', 'dimension3', activeView);

Once sufficient information is stored in Google Analytics, the activity can be segmented by different view types. The following screenshot shows an example of such a report.

Two view type segments compared

Conclusion

It has been only three years since Ethan Marcotte published Response Web Design and web designers started thinking “mobile first.” The techniques used to analyze website activity haven’t kept pace with the different approaches to creating user interfaces. By tracking user preferences, combining complementary data sources and making simple configuration changes, we can narrow the gap. We can use server-side techniques to improve efficiency, and we can use hard facts to make design decisions, rather than our perception alone.

Universal Analytics is a powerful tool, and it is prepared for a world in which designers get a single report of all interactions, including for websites, native applications and real-world events (such as visits to a physical store or interactions with a call center). We’ve explored the possibilities a bit in this article. Here are some features to consider exploring further:

  • See how users interact across multiple sessions.. If unique and anonymous user IDs are available in your web application, then use the “User ID” feature of Google Analytics to track users across multiple sessions. Be aware that your privacy policy must allow for this and must adhere to Google’s requirements for anonymity.
  • Track activity beyond the web.. Integrate Google Analytics in native applications alongside websites for a complete view of digital user behavior.
  • Track JavaScript events that don’t result in a full page refresh. Use events to track activity on a web page that does not result in a new page being loaded.

Whatever you do next, a bit of simple customization and configuration can make a big difference to the insights you get from Google Analytics. Happy analyzing!

Smashing Editorial (da, ml, al)