/ajax/libs/jquery-scrolldepth/0.7.1/jquery.scrolldepth.js
JavaScript | 260 lines | 157 code | 60 blank | 43 comment | 46 complexity | e37d6b7fcd5668bb4b27e47304262624 MD5 | raw file
- /*!
- * @preserve
- * jquery.scrolldepth.js | v0.7.1
- * Copyright (c) 2014 Rob Flaherty (@robflaherty)
- * Licensed under the MIT and GPL licenses.
- */
- ;(function ( $, window, document, undefined ) {
- "use strict";
- var defaults = {
- minHeight: 0,
- elements: [],
- percentage: true,
- userTiming: true,
- pixelDepth: true,
- nonInteraction: true
- };
- var $window = $(window),
- cache = [],
- lastPixelDepth = 0,
- universalGA,
- classicGA,
- standardEventHandler;
- /*
- * Plugin
- */
- $.scrollDepth = function(options) {
- var startTime = +new Date;
- options = $.extend({}, defaults, options);
- // Return early if document height is too small
- if ( $(document).height() < options.minHeight ) {
- return;
- }
- /*
- * Determine which version of GA is being used
- * "ga", "_gaq", and "dataLayer" are the possible globals
- */
- if (typeof ga === "function") {
- universalGA = true;
- }
- if (typeof _gaq !== "undefined" && typeof _gaq.push === "function") {
- classicGA = true;
- }
- if (typeof options.eventHandler === "function") {
- standardEventHandler = options.eventHandler;
- } else if (typeof dataLayer !== "undefined" && typeof dataLayer.push === "function") {
- standardEventHandler = dataLayer.push;
- }
- if (options.percentage) {
- // Establish baseline (0% scroll)
- sendBaseline('Percentage');
- } else if (options.elements) {
- sendBaseline('Elements');
- }
- /*
- * Functions
- */
- /*
- * Putting this in a separate function because the Baseline event may soon be removed entirely
- */
- function sendBaseline(action, label) {
- if (standardEventHandler) {
- standardEventHandler({'event': 'ScrollDistance', 'eventCategory': 'Scroll Depth', 'eventAction': action, 'eventLabel': 'Baseline', 'eventValue': 1, 'eventNonInteraction': true });
- } else {
- if (universalGA) {
- ga('send', 'event', 'Scroll Depth', action, 'Baseline', 1, {'nonInteraction': true });
- }
- if (classicGA) {
- _gaq.push(['_trackEvent', 'Scroll Depth', action, 'Baseline', 1, true]);
- }
- }
- }
- function sendEvent(action, label, scrollDistance, timing) {
- if (standardEventHandler) {
- standardEventHandler({'event': 'ScrollDistance', 'eventCategory': 'Scroll Depth', 'eventAction': action, 'eventLabel': label, 'eventValue': 1, 'eventNonInteraction': options.nonInteraction});
- if (options.pixelDepth && arguments.length > 2 && scrollDistance > lastPixelDepth) {
- lastPixelDepth = scrollDistance;
- standardEventHandler({'event': 'ScrollDistance', 'eventCategory': 'Scroll Depth', 'eventAction': 'Pixel Depth', 'eventLabel': rounded(scrollDistance), 'eventValue': 1, 'eventNonInteraction': options.nonInteraction});
- }
- if (options.userTiming && arguments.length > 3) {
- standardEventHandler({'event': 'ScrollTiming', 'eventCategory': 'Scroll Depth', 'eventAction': action, 'eventLabel': label, 'eventTiming': timing});
- }
- } else {
- if (universalGA) {
- ga('send', 'event', 'Scroll Depth', action, label, 1, {'nonInteraction': options.nonInteraction});
- if (options.pixelDepth && arguments.length > 2 && scrollDistance > lastPixelDepth) {
- lastPixelDepth = scrollDistance;
- ga('send', 'event', 'Scroll Depth', 'Pixel Depth', rounded(scrollDistance), 1, {'nonInteraction': options.nonInteraction});
- }
- if (options.userTiming && arguments.length > 3) {
- ga('send', 'timing', 'Scroll Depth', action, timing, label);
- }
- }
- if (classicGA) {
- _gaq.push(['_trackEvent', 'Scroll Depth', action, label, 1, options.nonInteraction]);
- if (options.pixelDepth && arguments.length > 2 && scrollDistance > lastPixelDepth) {
- lastPixelDepth = scrollDistance;
- _gaq.push(['_trackEvent', 'Scroll Depth', 'Pixel Depth', rounded(scrollDistance), 1, options.nonInteraction]);
- }
- if (options.userTiming && arguments.length > 3) {
- _gaq.push(['_trackTiming', 'Scroll Depth', action, timing, label, 100]);
- }
- }
- }
- }
- function calculateMarks(docHeight) {
- return {
- '25%' : parseInt(docHeight * 0.25, 10),
- '50%' : parseInt(docHeight * 0.50, 10),
- '75%' : parseInt(docHeight * 0.75, 10),
- // 1px cushion to trigger 100% event in iOS
- '100%': docHeight - 5
- };
- }
- function checkMarks(marks, scrollDistance, timing) {
- // Check each active mark
- $.each(marks, function(key, val) {
- if ( $.inArray(key, cache) === -1 && scrollDistance >= val ) {
- sendEvent('Percentage', key, scrollDistance, timing);
- cache.push(key);
- }
- });
- }
- function checkElements(elements, scrollDistance, timing) {
- $.each(elements, function(index, elem) {
- if ( $.inArray(elem, cache) === -1 && $(elem).length ) {
- if ( scrollDistance >= $(elem).offset().top ) {
- sendEvent('Elements', elem, scrollDistance, timing);
- cache.push(elem);
- }
- }
- });
- }
- function rounded(scrollDistance) {
- // Returns String
- return (Math.floor(scrollDistance/250) * 250).toString();
- }
- /*
- * Throttle function borrowed from:
- * Underscore.js 1.5.2
- * http://underscorejs.org
- * (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
- * Underscore may be freely distributed under the MIT license.
- */
- function throttle(func, wait) {
- var context, args, result;
- var timeout = null;
- var previous = 0;
- var later = function() {
- previous = new Date;
- timeout = null;
- result = func.apply(context, args);
- };
- return function() {
- var now = new Date;
- if (!previous) previous = now;
- var remaining = wait - (now - previous);
- context = this;
- args = arguments;
- if (remaining <= 0) {
- clearTimeout(timeout);
- timeout = null;
- previous = now;
- result = func.apply(context, args);
- } else if (!timeout) {
- timeout = setTimeout(later, remaining);
- }
- return result;
- };
- }
- /*
- * Scroll Event
- */
- $window.on('scroll.scrollDepth', throttle(function() {
- /*
- * We calculate document and window height on each scroll event to
- * account for dynamic DOM changes.
- */
- var docHeight = $(document).height(),
- winHeight = window.innerHeight ? window.innerHeight : $window.height(),
- scrollDistance = $window.scrollTop() + winHeight,
- // Recalculate percentage marks
- marks = calculateMarks(docHeight),
- // Timing
- timing = +new Date - startTime;
- // If all marks already hit, unbind scroll event
- if (cache.length >= 4 + options.elements.length) {
- $window.off('scroll.scrollDepth');
- return;
- }
- // Check specified DOM elements
- if (options.elements) {
- checkElements(options.elements, scrollDistance, timing);
- }
- // Check standard marks
- if (options.percentage) {
- checkMarks(marks, scrollDistance, timing);
- }
- }, 500));
- };
- })( jQuery, window, document );