analytics-scroll-depth
Version:
183 lines (165 loc) • 5.56 kB
JavaScript
/*!
* Analytics Scroll Depth v0.1.2
* (c) 2017 Nick Mickley
* Released under the MIT License.
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.AnalyticsScrollDepth = factory())
}(this, (() => {
function throttle(fcn, threshhold) {
const _this = this
let last = 0
let timeout = null
return function () {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key]
}
const time = new Date().getTime()
if (time < last + threshhold) {
clearTimeout(timeout)
timeout = setTimeout(() => {
last = time
fcn.apply(_this, args)
}, threshhold)
} else {
last = time
fcn.apply(_this, args)
}
}
}
const index = function () {
let settings = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}
settings = Object.assign({
throttle : 250,
minHeight : 0,
scrollElement : document.documentElement,
percentages : [0.25, 0.5, 0.75, 0.9, 0.95, 0.99],
pixelDepthInterval : 500,
elements : [],
dataLayer : window.dataLayer,
trackerName : '',
eventName : 'CustomEvent',
eventCategory : 'Scroll Depth',
percentageDepthAction : 'Percentage Depth',
pixelDepthAction : 'Pixel Depth',
elementAction : 'Element Depth',
nonInteraction : true,
}, settings)
let greatestScrollTop = 0
if (settings.trackerName) {
settings.trackerName += '.'
}
/**
* Send event dataLayer and/or analytics object
* @param {object} o Object to send to dataLayer
* @return {void}
*/
function send(o) {
if (settings.dataLayer) {
settings.dataLayer.push(o)
} else if (window[window.GoogleAnalyticsObject]) {
window[window.GoogleAnalyticsObject](`${settings.trackerName}send`, 'event', o.eventCategory, o.eventAction, o.eventLabel, o.eventValue, { nonInteraction: o.nonInteraction })
}
}
/**
* Push an event when the user has scrolled past each percentage in settings.percentages
* @return {void}
*/
function percentageDepth() {
const scrollRatio = settings.scrollElement.scrollTop / (settings.scrollElement.scrollHeight - settings.scrollElement.clientHeight) // eslint-disable-line max-len
settings.percentages.forEach((point, index) => {
if (scrollRatio >= point) {
settings.percentages.splice(index, 1)
send({
event : settings.eventName,
eventCategory : settings.eventCategory,
eventAction : settings.percentageDepthAction,
eventLabel : point,
eventValue : null,
nonInteraction : settings.nonInteraction,
})
}
})
}
/**
* Push an event when the user has scrolled past each pixelDepthInterval
* @return {void}
*/
function pixelDepth() {
const scrollTop = settings.scrollElement.scrollTop
while (scrollTop >= greatestScrollTop + settings.pixelDepthInterval) {
greatestScrollTop += settings.pixelDepthInterval
send({
event : settings.eventName,
eventCategory : settings.eventCategory,
eventAction : settings.pixelDepthAction,
eventLabel : greatestScrollTop + settings.pixelDepthInterval,
eventValue : null,
nonInteraction : settings.nonInteraction,
})
}
}
/**
* Return the event label
* @param {node} element
* @return {string}
*/
function formatElementLabel(element) {
let label = element.localName
if (element.id) {
label += `#${element.id}`
}
if (element.className) {
label += `.${element.className.replace(/ /g, '.')}`
}
return label
}
/**
* Push an event when the given element scrolls into view, if it exists
* @return {void}
*/
function elements() {
settings.elements.forEach((element, index) => {
if (element && element.offsetTop && element.clientHeight && element.offsetTop + element.clientHeight < settings.scrollElement.clientHeight + settings.scrollElement.scrollTop) {
settings.elements.splice(index, 1)
send({
event : settings.eventName,
eventCategory : settings.eventCategory,
eventAction : settings.elementAction,
eventLabel : formatElementLabel(element),
eventValue : null,
nonInteraction : settings.nonInteraction,
})
}
})
}
/**
* Scroll watcher
* @return {void}
*/
function onScroll() {
if (settings.percentages) {
percentageDepth()
}
if (settings.pixelDepthInterval) {
pixelDepth()
}
if (settings.elements.length) {
elements()
}
}
/**
* Set scrollw atcher
* @return {void}
*/
function watch() {
window.addEventListener('scroll', throttle(onScroll, settings.throttle), true)
}
if (settings.scrollElement.scrollHeight > settings.scrollElement.clientHeight && settings.scrollElement.scrollHeight > settings.minHeight) {
watch()
}
}
return index
})))