A while ago I wrote Advanced Content Tracking – a post about how to measure if users are actually reading your content. I’ve been getting a lot of requests to update this code for Universal Analytics.
So here it is – an updated script specifically for use with Universal Analytics.
This Google Analytics customization collects data as users scroll down a page. It uses events to track when a post loads, when the user scrolls more than 150 pixels, when the user reaches the bottom of the content and when the user reaches the bottom of the page.

This technique uses Google Analytics events to track a user as they scroll down a page of content.
The end result is some cool data about how many users actually read content. Here’s a sample of what the data looks like. This is just an basic event report with the Event Action and Event Label.

You can access the Reading data in your Event reports. Here we see a single article and how often users scrolled, read the whole article and got to the bottom of the page.
The Scroll Tracking Code
Here is the JavaScript code that measures user scrolling.
TIP – You can use the tabs at the top of the code window to try the script. Just click on Result.
What’s changed in this version?
First, the blog post title is now collected as part of the event. Specifically I’m pulling the page title from the HTML and putting it into the event label. This makes it easier to drill down and see which pages people are reading. This was possible before using the Page Title dimension, but using the event label makes it just a bit easier. See the image above.
Another thing I change is I now use a Custom Dimension rather than a Custom Variable, to collect the ‘reader type’. Custom variables do not exist in Universal Analytics.
This change will impact your data! You will no longer see data in the Custom Variables report – because you’re not using Custom Variables. Custom Dimensions are only available in Custom Reports and Custom Dashboards.
I also changed how the Custom Dimensions are set. This script will set a Custom Dimension when the user reaches the bottom of the post content – not the bottom of the page. When they reach the bottom of the content they are categorized as a scanner or a reader.
- A scanner is someone that simple scrolls to the bottom of the content in less than 60 seconds.
- A reader is someone that take more than 60 seconds to reach the bottom of the content.
This is hardly a scientific way to categorize users, but it works for me :)
Finally, I added three custom metrics to store the time metrics: time to scroll, time to content bottom and time to page bottom.
Remember, in order to configure Custom Dimensions and Custom Metric you must first add them via your Google Analytics admin settings.
Other than the above changes the functionality is still the same.
Implementing the code
Step 1: There are a few code changes that you must make in order for this code to work on YOUR site.
1. Turn off debugging: This flag will display alert messages, rather than send GA data, when the user scrolls, reaches the bottom of the content and reaches the bottom of the page. If you do not set this to FALSE your users will get all sorts of alert messages :)
2. Decide how far you want for scroll depth: I send an event after the user scrolls 150 px. You can change this value, but I believe it works fine and does capture user engagement.
3. Specify where the bottom of your content is: This is the most important setting. This script sends an event when the user gets to the bottom of a post. That’s determined by the HTML. For me, the HTML is identified as .entry-content
, as shown in this code.
[code lang=”js”]
if (bottom >= $(‘.entry-content’).scrollTop() + $(‘.entry-content’).innerHeight() && !endContent) {
[/code]
You must change this line of code to identify a piece of HTML on your site that signifies the end of the content. This is the hardest part of the implementation.
Step 2: Add the code before the closing [code][/code] on your site. Make sure it appears AFTER the Universal Analytics page tag. It should look something like this when complete:
[code lang=”js”]
<head>
… all sorts of tags …
<script>
//
// Universal Analytics page tag
//
(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’, ‘UA-XXXXXX-YY’);
ga(‘send’, ‘pageview’);
//
// Scroll tracking script
//
jQuery(function($) {
// Debug flag
var debugMode = true;
// Default time delay before checking location
var callBackTime = 100;
// # px before tracking a reader
var readerLocation = 150;
// Set some flags for tracking & execution
var timer = 0;
var scroller = false;
var endContent = false;
var didComplete = false;
… More code here …
</script>
[/code]
That should be it. You should see data instantly in the Real Time Event reports.
I encourage you to read the instructions in my original post.
Finally, a lot of people have asked me about implementing this script with Google Tag Manager. This really warms my heart :) I love tag management!
You can use this script with Google Tag Manager – but it takes a bit of work. I’ll write a separate post on that topic.
That’s it. I hope you find this script useful. Feel free to modify it to fit your needs. I’ve really enjoyed the data that it generates – it’s helped me better understand my readers and content.
Hey Justin, great info. I also wrote a post on using GTM + GA to track scroll depth, without having to make any code changes on the site: http://andygibson.us/2013/10/track-scroll-depth-using-google-tag-manager/
@Andy: Thanks for sharing! I too have a GTM version of this. But it uses some super secret stuff that will make implementation easy. No on-page code, no plugins. Just GTM :) I hope to post by the end of the month.
Just getting started with GTM. What are some good reads?
@Ray: I would start with the GTM help center. It has a few good articles about the core concepts. I also have a couple of posts here on the site:
All About Google Tag Manager
Getting Started with Google Tag Manager
Implementing Google Analytics with Google Tag Manager
Preview and Publishing Tags with Google Tag Manager
Make Analytics Better with Tag Management and a Data Layer
I was just about to ask about GTM version for this, can’t wait.
Justin, do you think that with 5+ hits per page (1 pageview, 4 events you just described, plus all other cool stuff like weather :-), you could get easily into troubles with hits-per-session quota – 500 at this moment if I am not mistaken.
@Peter: Potentially, but I don’t think this would be a major issue for most publishers. Even with an extreme 10 pv/v, that would be 50 total hits. Let’s say that you’re also doing some other really unique tracking that doubles that – now we’re at 100 hits. Still well within the limits. But, as always, test things out for your particular situation.
you are not able to tell if someone immediately saves your page to Pocket and reads it there? it just looks like they land on the page and bounced out?
@Keith: That’s a bit harder. I’m going to assume that they use a browser extension to save the article to pocket. In that case we can’t do too much. We can’t capture actions in a browser extension.
However, if you have a “Save to Pocket” button on each post, then you could add some Event tracking to that button. The event would track how often people save the item to pocket and you could also use the event to stop GA from measuring a bounce.
Speaking of Google Tag Manager, is it true that the only way to use “Adjusted bounce rate” now is with Google Tag Manager? It used to be a simple addition to the old ga.js script but I’ve not seen it implemented with the analytics.js script in Universal Analytics.
Great post as usual.
@BDev: No, you can still implement Adjusted Bounce Rate with analytics.js. The only thing that has changed is the syntax of the code. But I’m not a fan of Adjusted Bounce Rate. I don’t think you should just arbitrarily send data based on some time. That’s why I use this script – it includes an actual physical trigger (scrolling) to prove the user is engaging.
If an event is sent each time an article is loaded, wouldn’t that already count as a second hit and not a bounce, even if the user doesn’t interact with the page (doesn’t start reading, scrolling, etc.)?
@Anna: Not necessarily. There are two kinds of events: interactive and non-interactive. Interactive events will change the bounce rate calculation. Non-interactive events will NOT change the bounce rate calculation. The event that tracks when an article is loaded is non-interactive.
We migrated to Tag Manager recently. But before implementing it on our clients profiles we tried it out on our own site first and works perfectly. After reading your blog posts on advanced content tracking I am really keen on implementing it, but through tag manager. Could you maybe give some pointers on how to create tags through Tag Manager?
Thank you, and great posts!
@Tiaan: Basically you need to put the scrolling code in a Custom HTML tag. However, you also need to include the standard UA tag in the custom HTML tag. This will insure that Google Analytics loads prior to the scroll tracking script. I’m working on another post that will explain this. Look for it soon.
Hi Justin, that’s a great & usefull post. I’m really looking forward being able to implement it with Goole Tag Manager!
Bertrand, from Volumium