cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - ActiveLovehtmlicon-new-collectionicon-personicon-teamlog-outoctocatpop-outspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External CSS

These stylesheets will be added in this order and before the code you write in the CSS editor. You can also add another Pen here, and it will pull the CSS from it. Try typing "font" or "ribbon" below.

Quick-add: + add another resource

Add External JavaScript

These scripts will run in this order and before the code in the JavaScript editor. You can also link to another Pen here, and it will run the JavaScript from it. Also try typing the name of any popular library.

Quick-add: + add another resource

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
              <!-- this is the usual Google Analytics Embed code with the trackPageView call removed. Angulartics module should make that call instead -->

<script>
  (function(i, s, o, g, r, a, m) {
    i['GoogleAnalyticsObject'] = r;
    i[r] = i[r] || function() {
      (i[r].q = i[r].q || []).push(arguments)
    }, i[r].l = 1 * new Date();
    a = s.createElement(o),
      m = s.getElementsByTagName(o)[0];
    a.async = 1;
    a.src = g;
    m.parentNode.insertBefore(a, m)
  })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');

  ga('create', 'UA-8842961-2', 'auto');
</script>

<!-- The Google Experiments tracking code is not embedded here as Google recommends. The Google Experiments module for Angular takes care of loading that later -->

<h1>Let's try Google Experiments A/B Testing with AngularJS</h1>

<!-- Here's a controller so I have place to hook up a button -->
<div ng-app="LetsTryGoogleExperiments" ng-controller="ContentController as vm">

  <!-- set up 2 possible content variations. Google Analytics will choose a variation to show, and the googleExperiments module's variation directive will hide the other variations -->

  <!-- use ng-cloak class to hide content while Google Analytics is loading and initializing -->
  <div variation="0" class="ng-cloak">
    <p style="color:blue">I am the <b>original</b> content.</p>
  </div>

  <div variation="1" class="ng-cloak">
    <p style="color:green">I am <b>variation 1.</b></p>
  </div>

  <button ng-click="vm.onButtonClick()">Clicking me fires an event which is counted as a conversion in Google Analytics</button>

</div>

<hr>

<h2>What does this do?</h2>

<p>A bunch of Angular modules are working together to show and hide different content on this page, and track which variation of the content results in the most button clicks. This technique is useful for optimizing websites to achieve their goals (sales, sign-ups, lower bounce rates etc).</p>

<p>I'm mostly interested in seeing <a href="https://github.com/omouse/angular-google-experiments">angular-google-experiments</a> in action with this page.</p>

<h2>How do I see it work?</h2>
<p>Assuming you have access to my GA account (you probably don't):</p>
<ol>
  <li>open the <a href="https://s.codepen.io/adamsullovey/debug/QEAaBw">debug version of this Pen</a> in a new incognito/private browsing window</li>
  <li>take note of which variation of the content that appears above the button</li>
  <li>click the button if you want</li>
  <li>close the browser window</li>
</ol>

<p>Repeat this process ~5 times and watch as Google Analytics chooses a variation to show you. Behind the scenes, it is keeping track of which variation results in the most button clicks.</p>

<p>If you don't use an incognito version of the window, you should see the same content on every page load unless you clear your cookies. Google Experiments keeps the experience consistent for each visitor instead of swapping things on each of their visits.</p>

<p>Meanwhile, inside Google Analytics reports you can watch the # of conversions in realtime under the Realtime > Conversions section. After enough data has been gathered (probably after 100s of clicks on the button), some experiment data should be visible
  under the Behaviour > Experiments section.</p>

<h2>Limitations?</h2>

<ul>
  <li>Can I run multiple experiments at once in 1 app?</li>
  <li>Does this work well with UI router and other common modules?</li>
  <li>Can I use it for just swapping css classes instead of entire HTML elements?</li>
</ul>
            
          
!
            
              button {
  display:block;
  font-size:1rem;
  padding:0.5rem;
  border-radius:10px;
}

body {
  font-family:sans-serif;
  line-height:1.4;
  max-width:600px;
  margin:3rem auto;
}

h1, h2 {
  font-weight:normal;
}
            
          
!
            
              angular.module('LetsTryGoogleExperiments', [
  'angulartics', // loaded in the pen's settings
  'angulartics.google.analytics', // loaded in the pen's settings
  'googleExperiments' // hardedcoded in this pen below
])
/**
 * this controller is specific to this project. It just makes sure button clicks are sent to Google Analytics as events
 */
.controller('ContentController', function($analytics) {

  this.onButtonClick = () => $analytics.eventTrack('convert-click');

})

.config(function(googleExperimentsProvider) {
  googleExperimentsProvider.configure({
    // this id is provided by Google
    experimentId: '03dxWc-3QiugZYUEC5Zfhg'
  });
})

;

/**
 * This is the Google Experiments module from:
 * https://github.com/omouse/angular-google-experiments/blob/master/googleExperiments.js
 * I couldn't find it on a major CDN, so it is embedded here
 */

angular.module('googleExperiments', ['angularLoad']);

angular.module('googleExperiments').directive(
  'variation', ['googleExperiments', function(googleExperiments) {
    return function(scope, element, attr) {
      element.addClass('ng-cloak');
      scope.$watch(attr.variation, function googleExperimentsVariationWatchAction(value) {
        googleExperiments.getVariation().then(function(variation) {
          if (variation == value) {
            element.removeClass('ng-cloak');
            element.removeClass('ng-hide');
          } else {
            element.addClass('ng-hide');
          }
        });
      });
    };
  }]
);;
angular.module('googleExperiments').provider(
  'googleExperiments', [function googleExperimentsProvider() {
    var variation;
    this.configure = function(conf) {
      this.config = conf;
    };

    this.$get = ['$q', '$timeout', 'angularLoad', function($q, $timeout, angularLoad) {
      var variationDeferred = $q.defer();

      angularLoad.loadScript('//www.google-analytics.com/cx/api.js?experiment=' + this.config.experimentId).then(function() {
        variationDeferred.resolve(cxApi.chooseVariation());
      }).catch(function() {
        //error loading script
      });

      return {
        getVariation: function() {
          return variationDeferred.promise;
        }
      };
    }];
  }]
);

/**
* I couldn't find angular-load.js on a major CDN, so it is embedded here too
* https://github.com/urish/angular-load/blob/master/angular-load.js
*/

/* angular-load.js / v0.4.1 / (c) 2014, 2015, 2016 Uri Shaked / MIT Licence */

(function() {
  'use strict';

  angular.module('angularLoad', [])
    .service('angularLoad', ['$document', '$q', '$timeout', function($document, $q, $timeout) {
      var document = $document[0];

      function loader(createElement) {
        var promises = {};

        return function(url) {
          if (typeof promises[url] === 'undefined') {
            var deferred = $q.defer();
            var element = createElement(url);

            element.onload = element.onreadystatechange = function(e) {
              if (element.readyState && element.readyState !== 'complete' && element.readyState !== 'loaded') {
                return;
              }

              $timeout(function() {
                deferred.resolve(e);
              });
            };
            element.onerror = function(e) {
              $timeout(function() {
                deferred.reject(e);
              });
            };

            promises[url] = deferred.promise;
          }

          return promises[url];
        };
      }

      /**
       * Dynamically loads the given script
       * @param src The url of the script to load dynamically
       * @returns {*} Promise that will be resolved once the script has been loaded.
       */
      this.loadScript = loader(function(src) {
        var script = document.createElement('script');

        script.src = src;

        document.body.appendChild(script);
        return script;
      });

      /**
       * Dynamically loads the given CSS file
       * @param href The url of the CSS to load dynamically
       * @returns {*} Promise that will be resolved once the CSS file has been loaded.
       */
      this.loadCSS = loader(function(href) {
        var style = document.createElement('link');

        style.rel = 'stylesheet';
        style.type = 'text/css';
        style.href = href;

        document.head.appendChild(style);
        return style;
      });
    }]);
})();
            
          
!
999px
Loading ..................

Console