Web analytics

GTM script for measuring scrolling in single page applications


Why did I program it myself?

  • I have a SPA (Single page application) around me, where the classic scroll just doesn’t work. It is loaded once and it goes out. You can’t control it.
  • I don’t want Jquery because it’s useless for me today and I like vanilla without addictions mess.
  • I don’t want any ballast like the codes for super old GA classic etc. just pure GTM.
  • I don’t want to send a scroll more than once for a given distance on the page, that is, if I don’t want to.
  • I send an event interactive only if the user scrolls. at least one whole page, for pages that are too small I won’t turn on scroll because the page is too small.
  • I want my page length data in the number of visible screens etc

The code consists of two parts… initial setting + reset of values ​​and the function itself by measuring the scroll. If I go to a new virtual page, all I have to do is send a reset after loading the page (“pagescrollinit ()” function) and I’m done, the script measures even if I have a single page application that doesn’t change the URL or reload the whole page. Of course, you can re-fire the entire script and reset it as well. The script measures 50% and 90% scroll and scroll through the entire first page, which cancels the bounce rate. The script is created for clarity and minimalism, yes it can be extended, for example, for the brand to 25% or 75%, just copy a few pieces of code. You can try it yourself.

Measuring script

  // (bonus) Part1: HTML INIT  on .* event with prioprity one per page
var pagescroll ={};
pagescrollinit(); // call for reset scroll in SPA

function pagescrollinit(){
	pagescroll = {
		min: 0.42, // measure only if doc have more than 2 pages of viewport. // yes 42
		sc50: false, // scroll 50%
		sc90: false, // scroll 90%
		sc2pg: false, // scroll 2 viewports
		sclstop: 0,
function pagescrollinit2(){
	pagescroll.DocSize = Math.round(SCViewportHeight() / SCDocHeight() * 10) / 10; // viewport / doc Height
	pagescroll.DocSizeName = SCViewportHeight() / SCDocHeight() < pagescroll.min ? "long-doc" : "too-small";
	pagescroll.DocPages = Math.round(SCDocHeight() / SCViewportHeight()); // how many viewports is long webpage
	pagescroll.DocCP = Math.round(100*SCCurrentPosition() / SCDocHeight())/100; // current position in %
	pagescroll.TooSmall=(SCViewportHeight() / SCDocHeight() > pagescroll.min);} // the page is too short to scroll

function SCDocHeight() {
	return void 0 !== document.height ? document.height : document.body.offsetHeight;
function SCCurrentPosition() {
	var e = "CSS1Compat" === (document.compatMode || "");
	return parseInt(void 0 !== window.pageXOffset ? window.pageYOffset : e ? document.documentElement.scrollTop : document.body.scrollTop, 10) + parseInt(SCViewportHeight(), 10);
function SCViewportHeight() {
	return "number" == typeof window.innerHeight ? a = window.innerHeight : document.documentElement && document.documentElement.clientHeight ? a = document.documentElement.clientHeight : document.body && document.body.clientHeight && (a = document.body.clientHeight);
// (bonus) Part2: on GA pageview callback
window.onscroll = function() {
	if (SCViewportHeight() / SCDocHeight() > pagescroll.min) {pagescroll.TooSmall = !0;}
	else {
		pagescroll.TooSmall = !1;
		var e = SCCurrentPosition() > pagescroll.sclstop ? !0 : !1; // the direction of scrolling
		pagescroll.sclstop = SCCurrentPosition();
		var sc50 = SCCurrentPosition() >= 0.5 * SCDocHeight() ? !0 : !1, // scroll 50%
			sc90 = SCCurrentPosition() >= 0.9 * SCDocHeight() ? !0 : !1, // scroll 90%
			sc2pg = SCCurrentPosition() >= 2 * SCViewportHeight() ? !0 : !1; // scroll page 2
		e && (sc50 && !pagescroll.sc50 && (window.dataLayer.push({event:"Scroll",eventAction:"50 %"}), pagescroll.sc50 = !0),
		sc90 && !pagescroll.sc90 && (window.dataLayer.push({event:"Scroll",eventAction:"90 %" }), pagescroll.sc90 = !0),
		sc2pg && !pagescroll.sc2pg && (window.dataLayer.push({event:"Scroll-2page"}), pagescroll.sc2pg = !0)

Variable with data

As you can see in the following picture, everything is nicely accessible via javascript and you can play with it and what is the main thing to read from the value.

You can also access the variables in GTM, just use the classic GTM javascript variable. For the example “pagescroll.DocPages” and the one according to the picture in this case returns the value 5, which says that the length of the user’s page is 5 viewport (screens).

What will be the output?

event: “Scroll-2page” to display the second page.

and distance measurement in percent

event: “Scroll” and eventAction: “90%” for a distance of 90% of the page.

Do I have it verified?

Yes, I run it on several sites for over a year.

Other tricks

You can divide the script into several parts, see note “(bonus)” and run the first part before sending pageview and send with pageview to GA enriched with a flag (GA metric) if it is too small a page and how long the page is in number of screens.

And the second part you run up to GA pageview callback and thus guarantee that it will never scroll before GA pageview is sent.

The mimistic reset of the scroll is then:


I deliberately put the scroll depth flags into individual variables, so you can ask at any time if “pagescroll.sc90” is True and you don’t have to go through the event map to find and check the current status, which in combination with SPA is quite rough.