Skip to content

Commit

Permalink
Update maxScrollTracker to use TrackerQueue
Browse files Browse the repository at this point in the history
  • Loading branch information
philipwalton committed Aug 18, 2018
1 parent 3eefed4 commit e613e54
Showing 1 changed file with 88 additions and 72 deletions.
160 changes: 88 additions & 72 deletions lib/plugins/max-scroll-tracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ import MethodChain from '../method-chain';
import provide from '../provide';
import Session from '../session';
import Store from '../store';
import TrackerQueue from '../tracker-queue';
import {plugins, trackUsage} from '../usage';
import {assign, createFieldsObj, debounce, isObject} from '../utilities';
import {assign, createFieldsObj, debounce, isObject, now} from '../utilities';


/**
Expand All @@ -37,9 +38,6 @@ class MaxScrollTracker {
constructor(tracker, opts) {
trackUsage(tracker, plugins.MAX_SCROLL_TRACKER);

// Feature detects to prevent errors in unsupporting browsers.
if (!window.addEventListener) return;

/** @type {MaxScrollTrackerOpts} */
const defaultOpts = {
increaseThreshold: 20,
Expand All @@ -50,27 +48,33 @@ class MaxScrollTracker {
// hitFilter: undefined
};

this.opts = /** @type {MaxScrollTrackerOpts} */ (
assign(defaultOpts, opts));

this.opts = /** @type {MaxScrollTrackerOpts} */ (assign(defaultOpts, opts));
this.tracker = tracker;
this.pagePath = this.getPagePath();

// Binds methods to `this`.
this.init = this.init.bind(this);
this.handleScroll = debounce(this.handleScroll.bind(this), 500);
this.trackerSetOverride = this.trackerSetOverride.bind(this);

// Creates the store and binds storage change events.
// Override the built-in tracker.set method to watch for changes.
MethodChain.add(tracker, 'set', this.trackerSetOverride);

this.pagePath = this.getPagePath();

this.store = Store.getOrCreate(
tracker.get('trackingId'), 'plugins/max-scroll-tracker');

// Creates the session and binds session events.
this.session = Session.getOrCreate(
tracker, this.opts.sessionTimeout, this.opts.timeZone);

// Override the built-in tracker.set method to watch for changes.
MethodChain.add(tracker, 'set', this.trackerSetOverride);
// Queue the rest of the initialization of the plugin idly.
this.queue = TrackerQueue.getOrCreate(tracker).add(this.init);
}

/**
* Idly initializes the rest of the plugin instance initialization logic.
*/
init() {
this.listenForMaxScrollChanges();
}

Expand All @@ -82,7 +86,7 @@ class MaxScrollTracker {
listenForMaxScrollChanges() {
const maxScrollPercentage = this.getMaxScrollPercentageForCurrentPage();
if (maxScrollPercentage < 100) {
window.addEventListener('scroll', this.handleScroll);
addEventListener('scroll', this.handleScroll);
}
}

Expand All @@ -91,53 +95,55 @@ class MaxScrollTracker {
* Removes an added scroll listener.
*/
stopListeningForMaxScrollChanges() {
window.removeEventListener('scroll', this.handleScroll);
removeEventListener('scroll', this.handleScroll);
}


/**
* Handles the scroll event. If the current scroll percentage is greater
* that the stored scroll event by at least the specified increase threshold,
* than the stored scroll event by at least the specified increase threshold,
* send an event with the increase amount.
*/
handleScroll() {
const pageHeight = getPageHeight();
const scrollPos = window.pageYOffset; // scrollY isn't supported in IE.
const windowHeight = window.innerHeight;

// Ensure scrollPercentage is an integer between 0 and 100.
const scrollPercentage = Math.min(100, Math.max(0,
Math.round(100 * (scrollPos / (pageHeight - windowHeight)))));

// If the max scroll data gets out of the sync with the session data
// (for whatever reason), clear it.
const sessionId = this.session.getId();
if (sessionId != this.store.get().sessionId) {
this.store.clear();
this.store.set({sessionId});
}
this.queue.add(({time}) => {
const pageHeight = getPageHeight();
const scrollPos = window.pageYOffset; // scrollY isn't supported in IE.
const windowHeight = window.innerHeight;

// Ensure scrollPercentage is an integer between 0 and 100.
const scrollPercentage = Math.min(100, Math.max(0,
Math.round(100 * (scrollPos / (pageHeight - windowHeight)))));

// If the max scroll data gets out of the sync with the session data
// (for whatever reason), clear it.
const sessionId = this.session.id;
if (sessionId != this.store.data.sessionId) {
this.store.clear();
this.store.update({sessionId});
}

// If the session has expired, clear the stored data and don't send any
// events (since they'd start a new session). Note: this check is needed,
// in addition to the above check, to handle cases where the session IDs
// got out of sync, but the session didn't expire.
if (this.session.isExpired(this.store.get().sessionId)) {
this.store.clear();
} else {
const maxScrollPercentage = this.getMaxScrollPercentageForCurrentPage();

if (scrollPercentage > maxScrollPercentage) {
if (scrollPercentage == 100 || maxScrollPercentage == 100) {
this.stopListeningForMaxScrollChanges();
}
const increaseAmount = scrollPercentage - maxScrollPercentage;
if (scrollPercentage == 100 ||
increaseAmount >= this.opts.increaseThreshold) {
this.setMaxScrollPercentageForCurrentPage(scrollPercentage);
this.sendMaxScrollEvent(increaseAmount, scrollPercentage);
// If the session has expired, clear the stored data and don't send any
// events (since they'd start a new session). Note: this check is needed,
// in addition to the above check, to handle cases where the session IDs
// got out of sync, but the session didn't expire.
if (this.session.isExpired(this.store.data.sessionId)) {
this.store.clear();
} else {
const maxScrollPercentage = this.getMaxScrollPercentageForCurrentPage();

if (scrollPercentage > maxScrollPercentage) {
if (scrollPercentage == 100 || maxScrollPercentage == 100) {
this.stopListeningForMaxScrollChanges();
}
const increaseAmount = scrollPercentage - maxScrollPercentage;
if (scrollPercentage == 100 ||
increaseAmount >= this.opts.increaseThreshold) {
this.setMaxScrollPercentageForCurrentPage(scrollPercentage);
this.sendMaxScrollEvent(increaseAmount, scrollPercentage, time);
}
}
}
}
});
}

/**
Expand Down Expand Up @@ -171,36 +177,43 @@ class MaxScrollTracker {
* Sends an event for the increased max scroll percentage amount.
* @param {number} increaseAmount
* @param {number} scrollPercentage
* @param {number} scrollTimestamp
*/
sendMaxScrollEvent(increaseAmount, scrollPercentage) {
/** @type {FieldsObj} */
const defaultFields = {
transport: 'beacon',
eventCategory: 'Max Scroll',
eventAction: 'increase',
eventValue: increaseAmount,
eventLabel: String(scrollPercentage),
nonInteraction: true,
};

// If a custom metric was specified, set it equal to the event value.
if (this.opts.maxScrollMetricIndex) {
defaultFields['metric' + this.opts.maxScrollMetricIndex] = increaseAmount;
}
sendMaxScrollEvent(increaseAmount, scrollPercentage, scrollTimestamp) {
this.queue.add(() => {
/** @type {FieldsObj} */
const defaultFields = {
transport: 'beacon',
eventCategory: 'Max Scroll',
eventAction: 'increase',
eventValue: increaseAmount,
eventLabel: String(scrollPercentage),
nonInteraction: true,
queueTime: now() - scrollTimestamp,
};

// If a custom metric was specified, set it equal to the event value.
if (this.opts.maxScrollMetricIndex) {
defaultFields['metric' + this.opts.maxScrollMetricIndex] =
increaseAmount;
}

this.tracker.send('event',
createFieldsObj(defaultFields, this.opts.fieldsObj,
this.tracker, this.opts.hitFilter));
this.tracker.send('event',
createFieldsObj(defaultFields, this.opts.fieldsObj,
this.tracker, this.opts.hitFilter));
});
}

/**
* Stores the current max scroll percentage for the current page.
* @param {number} maxScrollPercentage
*/
setMaxScrollPercentageForCurrentPage(maxScrollPercentage) {
this.store.set({
[this.pagePath]: maxScrollPercentage,
sessionId: this.session.getId(),
this.queue.add(() => {
this.store.update({
[this.pagePath]: maxScrollPercentage,
sessionId: this.session.id,
});
});
}

Expand All @@ -209,12 +222,12 @@ class MaxScrollTracker {
* @return {number}
*/
getMaxScrollPercentageForCurrentPage() {
return this.store.get()[this.pagePath] || 0;
return this.store.data[this.pagePath] || 0;
}

/**
* Gets the page path from the tracker object.
* @return {number}
* @return {string}
*/
getPagePath() {
const url = parseUrl(
Expand All @@ -226,7 +239,10 @@ class MaxScrollTracker {
* Removes all event listeners and restores overridden methods.
*/
remove() {
this.queue.destroy();
this.store.destroy();
this.session.destroy();

this.stopListeningForMaxScrollChanges();
MethodChain.remove(this.tracker, 'set', this.trackerSetOverride);
}
Expand Down

0 comments on commit e613e54

Please sign in to comment.