Skip to content

Commit

Permalink
Send scroll event to data layer #15098 (#15179)
Browse files Browse the repository at this point in the history
  • Loading branch information
stephaniehobson authored Oct 30, 2024
1 parent 087e994 commit 6f96cb8
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 1 deletion.
16 changes: 16 additions & 0 deletions media/js/base/datalayer-trackscroll-init.es6.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

import TrackScroll from './datalayer-trackscroll.es6';

// Create namespace
if (typeof window.Mozilla === 'undefined') {
window.Mozilla = {};
}

window.Mozilla.TrackScroll = TrackScroll;

window.addEventListener('scroll', TrackScroll.onScroll, false);
85 changes: 85 additions & 0 deletions media/js/base/datalayer-trackscroll.es6.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

if (typeof window.dataLayer === 'undefined') {
window.dataLayer = [];
}

const TrackScroll = {};

// track when page has been scrolled these percentages:
let thresholds = [25, 50, 75, 90];
let listening = true;

function debounce(func, delay) {
let timer;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, arguments);
}, delay);
};
}

// get what percentage of the page has been scrolled
TrackScroll.getDepth = (scrollHeight, innerHeight, scrollY) => {
return (scrollY / (scrollHeight - innerHeight)) * 100;
};

// log the event to the dataLayer
TrackScroll.sendEvent = (threshold) => {
window.dataLayer.push({
event: 'scroll',
percent_scrolled: String(threshold)
});
};

// removes the event listener after we're done
TrackScroll.removeListener = () => {
if (listening) {
window.removeEventListener('scroll', TrackScroll.scrollListener, false);
listening = false;
}
};

TrackScroll.scrollHandler = () => {
// check the browser supports filter before doing anything else
if (typeof Array.prototype.filter === 'function') {
const scrollHeight = document.documentElement.scrollHeight;
const innerHeight = window.innerHeight;
const scrollY = window.scrollY;
const currentDepth = TrackScroll.getDepth(
scrollHeight,
innerHeight,
scrollY
);

// get a list of thresholds we've passed
const matchingThresholds = thresholds.filter(
(threshold) => currentDepth >= threshold
);

// remove thresholds we've passed from list of ones we're looking for
thresholds = thresholds.filter((threshold) => currentDepth < threshold);

// send the event for thresholds we passed
matchingThresholds.forEach((threshold) => {
TrackScroll.sendEvent(threshold);
});

// remove the event listener if we've scrolled past all thresholds
if (thresholds.length === 0) {
TrackScroll.removeListener();
}
} else {
// if it's too old to support logging, remove the listener
TrackScroll.removeListener();
}
};

TrackScroll.onScroll = debounce(TrackScroll.scrollHandler, 100);

export default TrackScroll;
3 changes: 2 additions & 1 deletion media/static-bundles.json
Original file line number Diff line number Diff line change
Expand Up @@ -1588,7 +1588,8 @@
"files": [
"js/base/experiment-amo.es6.js",
"js/base/experiment-amo-init.es6.js",
"js/base/datalayer-productdownload-init.es6.js"
"js/base/datalayer-productdownload-init.es6.js",
"js/base/datalayer-trackscroll-init.es6.js"
],
"name": "data"
},
Expand Down
71 changes: 71 additions & 0 deletions tests/unit/spec/base/datalayer-trackscroll.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

/* For reference read the Jasmine and Sinon docs
* Jasmine docs: https://jasmine.github.io/
* Sinon docs: http://sinonjs.org/docs/
*/

import TrackScroll from '../../../../media/js/base/datalayer-trackscroll.es6.js';

describe('datalayer-trackscroll.es6.js', function () {
const scrollHeight = 1000;
const innerHeight = 200;
const scroll25 = 200;
const scroll50 = 400;
const scroll75 = 600;
const scroll90 = 720;

beforeEach(function () {
window.dataLayer = [];
});

afterEach(function () {
delete window.dataLayer;
});

it('calculates scroll depth correctly', function () {
const depth25 = TrackScroll.getDepth(
scrollHeight,
innerHeight,
scroll25
);
expect(depth25 === 25).toBeTruthy();
const depth50 = TrackScroll.getDepth(
scrollHeight,
innerHeight,
scroll50
);
expect(depth50 === 50).toBeTruthy();
const depth75 = TrackScroll.getDepth(
scrollHeight,
innerHeight,
scroll75
);
expect(depth75 === 75).toBeTruthy();
const depth90 = TrackScroll.getDepth(
scrollHeight,
innerHeight,
scroll90
);
expect(depth90 === 90).toBeTruthy();
});

it('correctly identifies when multiple scroll thresholds have been passed', function () {
spyOn(TrackScroll, 'getDepth').and.returnValue(80);
TrackScroll.scrollHandler();
expect(window.dataLayer[0]['percent_scrolled'] === '25').toBeTruthy();
expect(window.dataLayer[1]['percent_scrolled'] === '50').toBeTruthy();
expect(window.dataLayer[2]['percent_scrolled'] === '75').toBeTruthy();
expect(window.dataLayer[4]).toBeFalsy();
});

it('will append the scroll event to the dataLayer', function () {
TrackScroll.sendEvent('50');
expect(window.dataLayer[0]['event'] === 'scroll').toBeTruthy();
expect(window.dataLayer[0]['percent_scrolled'] === '50').toBeTruthy();
});
});

0 comments on commit 6f96cb8

Please sign in to comment.