Search code examples
javascriptgoogle-analyticsintercom

Which design pattern is used for Snippets like Google Analytics or Intercom?


I want to create a smiliar snippet to Google Analytics and Intercom:

Intercom example https://developers.intercom.com/installing-intercom/docs/basic-javascript

//Set your APP_ID
var APP_ID = "APP_ID";

window.intercomSettings = {
    app_id: APP_ID
  };
(function(){var w=window;var ic=w.Intercom;if(typeof ic==="function"){ic('reattach_activator');ic('update',w.intercomSettings);}else{var d=document;var i=function(){i.c(arguments);};i.q=[];i.c=function(args){i.q.push(args);};w.Intercom=i;var l=function(){var s=d.createElement('script');s.type='text/javascript';s.async=true;s.src='https://widget.intercom.io/widget/' + APP_ID;var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);};if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})();

or Google Analytics:


Adding analytics.js to Your Site
The analytics.js library is a JavaScript library for measuring how users interact with your website. This document explains how to add analytics.js to your site.

The JavaScript measurement snippet
Adding the following code (known as the "JavaScript measurement snippet") to your site's templates is the easiest way to get started using analytics.js.

The code should be added near the top of the <head> tag and before any other script or CSS tags, and the string 'GA_MEASUREMENT_ID' should be replaced with the property ID (also called the "measurement ID") of the Google Analytics property you wish to work with.

Tip: If you do not know your property ID, you can use the Account Explorer to find it.
<!-- Google Analytics -->
<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-XXXXX-Y', 'auto');
ga('send', 'pageview');
</script>
<!-- End Google Analytics -->

Which design patterns did Intercom and Google use to create this snippets? How are they working internally? Can you give me maybe a basic structure so I can fill it with the functionalites I want?


Solution

  • The IIFE of google analytics ( adn Intercom ) are basically just the shortest way they could write a script loader that will work for all browsers.

    If we write the shorthand fully, the structure becomes easier to read:

    window.GoogleAnalyticsObject = 'ga';
    window.ga = window.ga || function() {
        window.ga.q = window.ga.q || [];
        window.ga.q.push( arguments );
    },
    window.ga.l = 1 * new Date();
    var a = document.createElement( 'script' );
    var m = document.getElementsByTagName( 'script' );
    a.async = true;
    a.src = 'https://www.google-analytics.com/analytics.js';
    m.parenNode.insertBefore( a, m );
    
    window.ga('create', 'UA-XXXXX-Y', 'auto');
    window.ga('send', 'pageview');
    

    The Intercom example basically does the same.

    So this script guarantees first that the proper names exist on the window object. If the google analytics object already existed, it will be used, else it'll become that function that will save the data in an array. This prevents the script to be added multiple times, so if another plugin also tries to load the same script, they will share their GA instance.

    Then the script creates a new script tag and configures it to the url of the actual google analytics script. Once the newly created script tags gets added to the page, it will load the actual analytics.js script.

    Although I haven't looked at the fine details of the analytics.js, but i'm pretty sure it'll then unpack or merge the window.ga object we created and replace it with the actual analytics script.

    So all in all, this is a fancy way to write <script src="https://www.google-analytics.com/analytics.js"> that will work on old browsers and will make sure we do not have multiple instances of the script fighting eachother if multiples are loaded on a single page.

    The actual inner workings of the analytics.js script and the intercom widget script are not included in these loader scripts, so I'm not going to go into detail how those work, that should be a different question showing the lines of code of the actual analytics.js script.

    Edit:

    window.ga = window.ga || function() {

    this line makes sure that window.ga is a function. For simplicity, let's consider it's a page where ga does not exist yet.

    So when we call ga('create', 'UA-XXXXX-Y', 'auto'); and ga('send', 'pageview'); we run the following statements:

        window.ga.q = window.ga.q || [];
        window.ga.q.push( arguments );
    

    window.ga.q = window.ga.q || []; makes sure that window.ga.q is an array. And then window.ga.q.push( arguments ); pushes the arguments to it.

    So calling ga('create', 'UA-XXXXX-Y', 'auto'); and ga('send', 'pageview'); before the analytics script is loaded results in the following:

    window.ga.q = [
      [ 'create', 'UA-XXXXX-Y', 'auto' ],
      [ 'send', 'pageview' ]
    ];
    

    Remember, window.ga is a function, but function are also kind of objects in javascript. So we can add the property q to the function just the same as if window.ga was an object and the function will still work.

    Once the analytics script gets loaded, it will look at the window.ga.q array and run all those commands.

    The problem is, look at the analytics.js script. It's also written in the same style, so it'll take hours to rewrite it with all the shorthands replaced, so i woudl try to look for like the annotated non-minified source of that script if it's available somewhere.