Shoora
10/19/2018 - 6:43 AM

Send event to Google Analytics on scrolling past certain points on a page

Send event to Google Analytics on scrolling past certain points on a page

(function($, w, d) {
  function autoDetectGA() {
    if (typeof _gaq !== 'undefined') {
      return 'ga';
    }
    if (typeof ga === 'function') {
      return 'ua';
    }
    if (typeof dataLayer !== 'undefined') {
      return 'gtm';
    }
    return false;
  }

  var defaults = {
    elements: [],
    minHeight: 0,
    offset: 0, // Not used yet
    elem: 0,
    type: autoDetectGA(),  // set type format in which the events should be send ('ga', 'ua' and 'gtm). Support for Google Analytics, (Google) Universal Analytics, and Google Tag Manager
    percentage: true,
    testing: false
  },

  $w = $(w),
  $d = $(d),
  cache = [];

  /*
   * Plugin
   */

  $.scrollDepth = function(options) {
    
    var startTime = +new Date;

    options = $.extend({}, defaults, options);

    if (false === options.type) {
      // in case of no analytics, revert to testing so we can see what would have been sent
      typeof console !== 'undefined' && typeof console.log === 'function' && console.log('Switching to testing mode, no Google Analytics found.');
      options.testing = true;
    }

    // Return early if document height is too small
    if ( $d.height() < options.minHeight ) {
      return;
    }

    // Establish baseline (0% scroll)
    sendEvent('Percentage', 'Baseline');

    /*
     * Functions
     */

    function sendEvent(action, label, timing) {
      if (!options.testing && options.type == 'ga') {
        _gaq.push(['_trackEvent', 'Scroll Depth', action, label, 1, true]);

        if (arguments.length > 2) {
          _gaq.push(['_trackTiming', 'Scroll Depth', action, timing, label, 100]);
        }

      } 
      else if (!options.testing && options.type == 'ua') {
        ga('send', 'event', 'Scroll Depth', action, label, 1, {'nonInteraction': 1});

        if (arguments.length > 2) {
          ga('send', 'timing', 'Scroll Depth', action, timing, label);
        }

      } 
      else if (!options.testing && options.type == 'gtm') {
        dataLayer.push({'event':'GAscroll', 'eventCategory':'Scroll Depth', 'eventAction': action, 'eventLabel': label, 'eventValue': 1, 'eventNonInteraction': true});

        if (arguments.length > 2) {
          dataLayer.push({'event':'GAtiming', 'eventCategory':'Scroll Depth', 'eventAction': action, 'eventLabel': label, 'eventTiming': timing});
        }

      } 
      else {
        $('#console').html(action + ': ' + label + '</br>' + 'Timing: ' + timing);
      }

      if (options.testing) {
        typeof console !== 'undefined' && typeof console.log === 'function' && console.log('Testing mode; event to be sent: [type, action, label, timing]', options.type, action, label, timing);
      }
    }

    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 - 1
      };
    }

    function checkMarks(marks, scrollDistance, timing) {
      // Check each active mark
      $.each(marks, function(key, val) {
        if ( $.inArray(key, cache) === -1 && scrollDistance >= val ) {
          sendEvent('Percentage', key, 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, timing);
            cache.push(elem);
          }
        }
      });
    }

    /*
     * Scroll Event
     */

    $w.on('scroll.scrollDepth', function() {

      /*
       * We calculate document and window height on each scroll event to
       * account for dynamic DOM changes.
       */

      var docHeight = $d.height(),
        winHeight = parseInt(w.innerHeight ? w.innerHeight : $w.height(), 10),
        scrollDistance = $w.scrollTop() + winHeight,

        // Offset not being used yet
        offset = parseInt(winHeight * (options.offset / 100), 10),

        // 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) {
        $w.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);
      }
    });

  };

})( jQuery, window, document );
jQuery.scrollDepth();