A while back I looked in to implementing Google Tag manager so that marketing folks at my company could manage custom events in Google Analytics. Beyond custom events, I’m not sure what Google Tag Manager does (it’s been a while since I looked at it). Perhaps there are a bunch of great benefits that I am missing.

I ended up not deploying Tag Manager for the same reason that others would choose to deploy it: simplicity. For me, the best way to keep things simple is to avoid another application abstraction and just use code. So, I created a JavaScript module that allows me to easily add custom events. Below is the code for my Google Analytics custom events emitter module.

// ----------------------------------------------------
// ---------- GOOGLE ANALYTICS CUSTOM EVENTS ----------
// ----------------------------------------------------
    Assign an `analyticsEvents` property to a global `APP` object. This property
    stores our module. The immediately invoked function expression (IIFE)
    assigned to this module ensures that all code within the module is contained
    within its own scope (the module scope). jQuery is passed as an argument
    which is assigned to the `$` character. This way, we protect jQuery from
    conflicting with other libraries that also alias to the $ character.
APP.analyticsEvents = (function($) {

        Assign Google Analytics "Event Actions" to constants. This prevents us
        from passing the wrong event action to Google. In the case of a typo, we
        get an "undefined" error instead of not seeing an error at all.
    var APPLY = 'Apply',
        BROWSE = 'Browse',
        CLICK = 'Click',
        LOGIN = 'Login',
        REGISTER = 'Register',
        REQUEST = 'Request',
        SEARCH = 'Search',
        SUBMIT = 'Submit',
        SUBSCRIBE = 'Subscribe';

        All of our custom events are stored in an `events` object. Each key in
        the `events` object corresponds to a Google Analytics "Event Category".
    var events = {
            An array is assigned to each key in the `events` object. Each index
            of the array contains sub arrays. The sub arrays contain the actual

            The first index of the sub array is a `data-event` attribute that we
            add element(s) in the DOM. The Second index of the sub array is the
            Google Analytics "Event Action" (where our constants created above
            are used). The third and final index of the sub array is the Google
            Analytics "Event Label".
        'Account Actions': [
            ['[data-event="start-account-create"]', REGISTER, 'Start Account Registration Before Checkout'],
            ['[data-event="start-account-create-quote-checkout"]', REGISTER, 'Start Account Registration During Quote Checkout'],
            ['[data-event="create-account"]', REGISTER, 'Register Account Before Checkout'],
            ['[data-event="create-account-order-checkout"]', REGISTER, 'Register Account After Order Checkout'],
            ['[data-event="login-account"]', LOGIN, 'Login To Account Before Checkout'],
            ['[data-event="login-account-order-checkout"]', LOGIN, 'Login To Account During Order Checkout'],
            ['[data-event="login-account-quote-checkout"]', LOGIN, 'Login To Account During Quote Checkout'],
            ['[data-event="start-credit-application"]', APPLY, 'Start Credit Application']
        'Content Marketing': [
            ['[data-event="start-catalog-request"]', REQUEST, 'Start Catalog Request'],
            ['[data-event="start-white-paper-request"]', REQUEST, 'Start White Paper Request'],
            ['[data-event="start-utility-request"]', REQUEST, 'Start Worksheet Request'],
            ['[data-event="start-newsletter-subscription"]', SUBSCRIBE, 'Start Newsletter Subscription']
        'Shopping Actions': [
            ['[data-event="shop-categories"]', BROWSE, 'Shop By Category'],
            ['[data-event="shop-manufacturers"]', BROWSE, 'Shop By Manufacturer'],
            ['[data-event="search-site"]', SEARCH, 'Search Entire Site'],
            ['[data-event="search-category"]', SEARCH, 'Search Within Category'],
            ['[data-event="search-results"]', SEARCH, 'Search Within Results'],
            ['[data-event="add-to-cart"]', CLICK, 'Add Product To Cart'],
            ['[data-event="add-to-quote"]', CLICK, 'Add Product To Quote']
        'Quote Checkout': [
            ['[data-event="start-quote-checkout"]', CLICK, 'Start Quote Checkout'],
            ['[data-event="opc-quote-shipping-address"]', SUBMIT, 'Enter Shipping Address For Quote'],
            ['[data-event="opc-quote-shipping-method"]', SUBMIT, 'Select Shipping Method For Quote'],
            ['[data-event="opc-quote-complete"]', SUBMIT, 'Complete Quote Checkout']
        'Order Checkout': [
            ['[data-event="start-order-checkout"]', CLICK, 'Start Order Checkout'],
            ['[data-event="opc-guest-checkout"]', SUBMIT, 'Order Checkout As Guest'],
            ['[data-event="opc-shipping-address"]', SUBMIT, 'Enter Shipping Address For Order'],
            ['[data-event="opc-shipping-method"]', SUBMIT, 'Select Shipping Method For Order'],
            ['[data-event="opc-payment"]', SUBMIT, 'Enter Payment Information For Order'],
            ['[data-event="opc-complete"]', SUBMIT, 'Complete Order Checkout']
        'Quote Actions': [
            ['[data-event="edit-quote"]', CLICK, 'Edit Quote'],
            ['[data-event="remove-quote"]', CLICK, 'Remove Quote'],
            ['[data-event="convert-quote"]', CLICK, 'Convert Quote To Order']
        'Order Actions': [
            ['[data-event="start-rma-request"]', CLICK, 'Start RMA Request'],
            ['[data-event="start-reorder"]', CLICK, 'Start Reorder']

        The `init` method gets things rolling. This is called when the
        `DOMContentLoaded` event fires (which happens after the HTML document
        has been loaded and parsed by the browser).
    function init() {
            Below we assign each key ("Event Category") in the `events` object
            to an array using the `Object.keys` method. We then itterate through
            that array using the `forEach` method, executing an anonymous
            function that we pass the "Event Category" to as an argument. Inside
            that anonymous function, our module's `buildEvents` function is
            called with our category passed as the first argument and the array
            assigned to that category as the second argument.
        Object.keys(events).forEach(function(category) {
            buildEvents(category, events[category]);

        The `bindListeners` method binds our Google Analytics "Custom Event"
        function created in the `buildEvents` method below as a callback to the
        DOM node whose selector is passed as the first argument (explained
        further in the in the comments for the `buildEvents` function below).
    function bindListeners(selector, evt) {
        $(selector).on('click', evt);

        The `buildEvents` method is called inside the `init` method. Its
        purpose is to construct the "Custom Event" function that gets called
        when an a user interaction takes place.
    function buildEvents(categoryName, categoryEvents) {
        categoryEvents.forEach(function(item) {
                The `evt` variable holds our "Custom Event" function. This
                function is passed as an argument to the `bindListensers`
                function above. As noted above, this function gets called when
                a user interaction takes place.
            var evt = function() {
                    We wrap our "Custom Event" funciton in a try/catch block.
                    This way, if the event fails (because Google Analytics
                    fails to load, etc), we can notify ourselves.
                try {
                    ga('send', 'event', categoryName, item[1], item[2]);
                } catch (err) {
                        The `APP.logError` module, which is not covered in this
                        post, will notify us of an error.

                Finally, we call the `bindListeners` function. This binds the
                function stored in `evt` as a callback to be executed when a
                user interaction takes place. The first argument contains the
                a selector (our `data-events`) attribute. The second argument
                is our `evt` callback function.
            bindListeners(item[0], evt);

        Once the DOMContentLoaded event has fired, we execute our module's
        `init` function using jQuery's `ready` funciton.

Posted by: John Dugan