Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Further optimizing Google Analytics snippet #1660

Closed
wants to merge 25 commits into from

Conversation

BLooperZ
Copy link
Contributor

EDIT: it started to be a real mess in this post so i cleared it up

this is a shorter (and still safe) version of the google analytics snippet - 322 charcters against 346
it does exactly the same - except adding the script after the first script in the document rather than before it. only one function changed - insertBefore to appendChild to eliminate the need for one of the parameters, all the rest is done by syntax manipulation ONLY
this is a bit more readable version (same code just added whitespace:

(function (b, o, i, l) {
    GoogleAnalyticsObject = l;
    (b[l] = b[l] || function () {
        (b[l].q = b[l].q || []).push(arguments)}
    ).l = +new Date;
    o.getElementsByTagName(i)[0].parentNode.appendChild(o.createElement(i))
    .src='//www.google-analytics.com/analytics.js'}
(window, document, 'script', 'ga'));ga('create', 'UA-XXXXX-X', 'auto');ga('send', 'pageview');

let go line by line:
new (function (b, o, i, l) { old (function(b,o,i,l,e,r)
not much to say we need less parameters
new GoogleAnalyticsObject = l; old b.GoogleAnalyticsObject=l;
the window object is assumed if no object specified (except for conditional statements)
new (b[l] = b[l] || function () { old b[l] || (b[l] = function () {
shorter syntax and we can use it 2 lines later
new (b[l].q = b[l].q || []).push(arguments)} old (b[l].q = b[l].q || []).push(arguments)}
no change here
new ).l = +new Date; old );b[l].l = +new Date;
we use the functio0n it self as the object instead of referencing it
new

o.getElementsByTagName(i)[0].parentNode.appendChild(o.createElement(i))
.src='//www.google-analytics.com/analytics.js'}

old

e=o.createElement(i);r=o.getElementsByTagName(i)[0];
e.src='//www.google-analytics.com/analytics.js';
r.parentNode.insertBefore(e,r)}

by changing to appendChild we do not need reference anymore and we declare anything inside the functions - using the original value and saving the need for a parameter
new (window, document, 'script', 'ga'));ga('create', 'UA-XXXXX-X', 'auto');ga('send', 'pageview'); old (window, document, 'script', 'ga'));ga('create','UA-XXXXX-X','auto');ga('send','pageview');
also the same

I've checked this script with google analytics debugger, also with multiple trackers
it works (no reason why not)
@mathiasbynens - feel free to update your article about it if you like

@BLooperZ BLooperZ changed the title Slightly improved Google Analytics snippet Further optimizing Google Analytics snippet Jan 15, 2015
e=o.createElement(i);r=o.getElementsByTagName(i)[0];
e.src='//www.google-analytics.com/analytics.js';
r.parentNode.insertBefore(e,r)}(window,document,'script','ga'));
o.getElementsByTagName(i)[0].parentNode.appendChild(o.createElement(i)).src='//www.google-analytics.com/analytics.js'}

This comment was marked as abuse.

This comment was marked as abuse.

This comment was marked as abuse.

e.src='//www.google-analytics.com/analytics.js';
r.parentNode.insertBefore(e,r)}(window,document,'script','ga'));
ga('create','UA-XXXXX-X','auto');ga('send','pageview');
(function(b,o,i){b.GoogleAnalyticsObject='g';(b.g||(b.g=function(){(b.g.q=b.g.q||[]).push(arguments)})).l=+new Date;

This comment was marked as abuse.

getting back the option to set the variable name
@BLooperZ
Copy link
Contributor Author

here is versions of this optimized script to choose from:
(someone please check the size when gzipping this, i don't know how)

perfect solution - safe for all - same as in the pull request file - 322 characters

(function(b,o,i,l){GoogleAnalyticsObject=l;(b[l]=b[l]||function(){(b[l].q=b[l].q||[]).push(arguments)}).l=+new Date;
o.getElementsByTagName(i)[0].parentNode.appendChild(o.createElement(i)).src='//www.google-analytics.com/analytics.js'}
(window,document,'script','ga'));ga('create','UA-XXXXX-X','auto');ga('send','pageview');

if you're not going to change the variable name - 308 characters

(function(o,i){GoogleAnalyticsObject='ga';(ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)}).l=+new Date;
o.getElementsByTagName(i)[0].parentNode.appendChild(o.createElement(i)).src='//www.google-analytics.com/analytics.js'}
(document,'script'));ga('create','UA-XXXXX-X','auto');ga('send','pageview');

if your page MUST got head - 285 characters

(function(b,o,l){GoogleAnalyticsObject=l;(b[l]=b[l]||function(){(b[l].q=b[l].q||[]).push(arguments)}).l=+new Date;
o.head.appendChild(o.createElement('script')).src='//www.google-analytics.com/analytics.js'}
(window,document,'ga'));ga('create','UA-XXXXX-X','auto');ga('send','pageview');

if your page must got head and you don't care about costum variable name - 271 characters

(function(o){GoogleAnalyticsObject='ga';(ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)}).l=+new Date;
o.head.appendChild(o.createElement('script')).src='//www.google-analytics.com/analytics.js'}
(document));ga('create','UA-XXXXX-X','auto');ga('send','pageview');

@paulirish
Copy link
Member

I looked at this new version and compared it to the old. The key thing here is the gzip'd size as this will "always" be served with gzip and it commonly erases many of these hand-tuning improvements.

  • original GA snippet: 312 bytes, gzip
  • h5bp GA snippet: 301 bytes, gzip (Δ of 11 to orig)
  • blooperz GA snippet: 283 bytes, gzip (Δ of 18 to h5bp)

(I'm gzipping the entire index.html, but probably should be. gzip is cool)

Anyways, we end up with a gain of 18 bytes. Due to how TCP works, the boundaries that actually end up affecting download time are at something like 14KB, 44KB, 88KB.

image
[via]

Why? TCP-slow start, basically.

Check the next few slides from here: https://docs.google.com/presentation/d/1qoginKKkdrRpIPGjJakt1y17iXsIBSaInaHbR4JyCIk/present?slide=id.gc03305a_0207

image

image

tl;dr: Small differences in byte size will not have a significant impact when the total filesize of the payload is within these below buckets.

  • 0KB to 13KB
  • 16K to 41KB
  • 44KB to 99KB

The boundaries, therefore, where this sort of byte golfing does have impact are around: 14.2K, 42.7K, 99.8K.

(These calculations ignore the overhead of HTTP headers. Assuming initial_cwnd of 10 and MSS of 1460.)

(off-topic for this issue in particular, but the global disclaimer in this topic is that reducing requests is a better priority. And understanding the critical path is a more holistic way than targeting either bytes or requests.)

e.src='//www.google-analytics.com/analytics.js';
r.parentNode.insertBefore(e,r)}(window,document,'script','ga'));
ga('create','UA-XXXXX-X','auto');ga('send','pageview');
(function(b,o,i,l){GoogleAnalyticsObject=l;(b[l]=b[l]||function(){(b[l].q=b[l].q||[]).push(arguments)}).l=+new Date;

This comment was marked as abuse.

wrapped lines
@ghost
Copy link

ghost commented Apr 4, 2015

Personally loving @mamathiasbynens suggestion

@bnjmnt4n
Copy link

bnjmnt4n commented Apr 4, 2015

👍 to @mathiasbynens's idea!

@ramasilveyra
Copy link

I really like @mathiasbynens ga snippet.

@donovanglover
Copy link

Supporting @mathiasbynens because of its simplicity and use of async. (:

@xeniun
Copy link

xeniun commented Apr 5, 2015

So sorry because I'm not skilled with JS.
Google provide this code below as an alternative asynchronous snippet.

<script async src='//www.google-analytics.com/analytics.js'></script>
<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-XXXX-Y', 'auto');
ga('send', 'pageview');
</script>

Why can't we simply add defer to this snippet?

@mathiasbynens
Copy link
Member

@xeniun Are you saying Google provides that snippet? Where? I can’t find it here.

@xeniun
Copy link

xeniun commented Apr 5, 2015

Hi @mathiasbynens !
I'm relearning about new Google Universal Analytics because I need to import all my previous parameters and just today I read about this snippet here: https://developers.google.com/analytics/devguides/collection/analyticsjs/advanced in "Alternative Asynchronous Snippet" section.
I hope I have been of help.
Google is evolving and expanding rapidly their own style and help resources.

@mathiasbynens
Copy link
Member

@xeniun Thanks for the link! I wonder why they bothered to add the IE9 comment instead of just adding defer to the snippet.

That snippet indicates that the GoogleAnalyticsObject = 'ga'; part isn’t necessary, so we can remove it from our snippet:

<script>
  ga=function(){ga.q.push(arguments)};ga.q=[];ga.l=+new Date;
  ga('create','UA-XXXX-Y','auto');ga('send','pageview')
</script>
<script src="https://www.google-analytics.com/analytics.js" async defer></script>

@xeniun
Copy link

xeniun commented Apr 5, 2015

@mathiasbynens Just out of curiosity, is the same if we reverse the sequences of the two scripts?

@mathiasbynens
Copy link
Member

@xeniun Yep, that doesn’t really matter.

@paulirish
Copy link
Member

oh yeah. I'm on board with using async defer.

I think we should be explicit with the ga global and put a window. in front of it. Otherwise LGTM!

@mathiasbynens
Copy link
Member

@paulirish Like this?

<script>
  window.ga=function(){window.ga.q.push(arguments)};window.ga.q=[];window.ga.l=+new Date;
  window.ga('create','UA-XXXX-Y','auto');window.ga('send','pageview')
</script>
<script src="https://www.google-analytics.com/analytics.js" async defer></script>

Seems a bit much IMHO. I generally prefer making globals explicit in browser code by using window.foo too, but doing it consistently in this case adds quite a bit of unneeded overhead, and doing it inconsistently (e.g. only when the var is created) doesn’t make sense to me.

@FagnerMartinsBrack
Copy link
Contributor

Considering Paul's comment, is there any reason not to provide an unminified source of this snippet in the boilerplate?

@alrra
Copy link
Member

alrra commented Apr 19, 2015

@mathiasbynens Can you open a pull request? Thanks!

mathiasbynens added a commit that referenced this pull request Apr 20, 2015
With this snippet, modern browsers use `async`, older browsers (i.e.
IE8 & IE9) use `defer`.

IE8 and IE9 lack `async` support but they have a broken implementation
of `defer`. However, the brokenness doesn’t apply in this scenario
since no scripts depend on GA in the way jQuery UI depends on jQuery.
`async` is also not supported by the Android 2.3 browser, but that
browser does have a preload scanner to make up for it.

Once we drop support for IE8 and IE9, the `defer` attribute can be
omitted.

The only downside is that the snippet is not a pure JavaScript
solution anymore, meaning it cannot be moved or concatenated into a
`.js` file. On the other hand, no one seemed to be doing that anyway;
everyone just inlines the snippet into the HTML.

Ref. #1660 (comment)
@mathiasbynens
Copy link
Member

@alrra Done in #1696. I haven’t added window. yet like @paulirish since I’m not sure where it should be added. Adding it everywhere seems overkill, and anything else seems inconsistent. #1660 (comment)

@alrra
Copy link
Member

alrra commented Apr 20, 2015

Thanks @mathiasbynens! 💜

Closing in favor of #1696.

@alrra alrra closed this Apr 20, 2015
@alrra alrra added javascript and removed html labels Apr 20, 2015
mathiasbynens added a commit that referenced this pull request Apr 25, 2015
With this snippet, modern browsers use `async`, older browsers (i.e.
IE8 & IE9) use `defer`.

IE8 and IE9 lack `async` support but they have a broken implementation
of `defer`. However, the brokenness doesn’t apply in this scenario
since no scripts depend on GA in the way jQuery UI depends on jQuery.
`async` is also not supported by the Android 2.3 browser, but that
browser does have a preload scanner to make up for it.

Once we drop support for IE8 and IE9, the `defer` attribute can be
omitted.

The only downside is that the snippet is not a pure JavaScript
solution anymore, meaning it cannot be moved or concatenated into a
`.js` file. On the other hand, no one seemed to be doing that anyway;
everyone just inlines the snippet into the HTML.

Ref. #1660 (comment)
mathiasbynens added a commit that referenced this pull request Apr 27, 2015
With this snippet, modern browsers use `async`, older browsers (i.e.
IE8 & IE9) use `defer`.

IE8 and IE9 lack `async` support but they have a broken implementation
of `defer`. However, the brokenness doesn’t apply in this scenario
since no scripts depend on GA in the way jQuery UI depends on jQuery.
`async` is also not supported by the Android 2.3 browser, but that
browser does have a preload scanner to make up for it.

Once we drop support for IE8 and IE9, the `defer` attribute can be
omitted.

The only downside is that the snippet is not a pure JavaScript
solution anymore, meaning it cannot be moved or concatenated into a
`.js` file. On the other hand, no one seemed to be doing that anyway;
everyone just inlines the snippet into the HTML.

Ref. #1660 (comment)
mathiasbynens added a commit that referenced this pull request Feb 11, 2016
With this snippet, modern browsers use `async`, older browsers (i.e.
IE8 & IE9) use `defer`.

IE8 and IE9 lack `async` support but they have a broken implementation
of `defer`. However, the brokenness doesn’t apply in this scenario
since no scripts depend on GA in the way jQuery UI depends on jQuery.
`async` is also not supported by the Android 2.3 browser, but that
browser does have a preload scanner to make up for it.

Once we drop support for IE8 and IE9, the `defer` attribute can be
omitted.

The only downside is that the snippet is not a pure JavaScript
solution anymore, meaning it cannot be moved or concatenated into a
`.js` file. On the other hand, no one seemed to be doing that anyway;
everyone just inlines the snippet into the HTML.

Ref. #1660 (comment)
mathiasbynens added a commit that referenced this pull request Feb 11, 2016
With this snippet, modern browsers use `async`, older browsers (i.e.
IE8 & IE9) use `defer`.

IE8 and IE9 lack `async` support but they have a broken implementation
of `defer`. However, the brokenness doesn’t apply in this scenario
since no scripts depend on GA in the way jQuery UI depends on jQuery.
`async` is also not supported by the Android 2.3 browser, but that
browser does have a preload scanner to make up for it.

Once we drop support for IE8 and IE9, the `defer` attribute can be
omitted.

The only downside is that the snippet is not a pure JavaScript
solution anymore, meaning it cannot be moved or concatenated into a
`.js` file. On the other hand, no one seemed to be doing that anyway;
everyone just inlines the snippet into the HTML.

Ref. #1660 (comment)

Closes #1696.
eleanor-byhook pushed a commit to eleanor-byhook/html5-boilerplate that referenced this pull request Feb 29, 2016
With this snippet, modern browsers use `async`, older browsers (i.e.
IE8 & IE9) use `defer`.

IE8 and IE9 lack `async` support but they have a broken implementation
of `defer`. However, the brokenness doesn’t apply in this scenario
since no scripts depend on GA in the way jQuery UI depends on jQuery.
`async` is also not supported by the Android 2.3 browser, but that
browser does have a preload scanner to make up for it.

Once we drop support for IE8 and IE9, the `defer` attribute can be
omitted.

The only downside is that the snippet is not a pure JavaScript
solution anymore, meaning it cannot be moved or concatenated into a
`.js` file. On the other hand, no one seemed to be doing that anyway;
everyone just inlines the snippet into the HTML.

Ref. h5bp/html5-boilerplate#1660 (comment)

Closes #1696.
jeffreznik pushed a commit to jeffreznik/sw-test that referenced this pull request Oct 15, 2017
With this snippet, modern browsers use `async`, older browsers (i.e.
IE8 & IE9) use `defer`.

IE8 and IE9 lack `async` support but they have a broken implementation
of `defer`. However, the brokenness doesn’t apply in this scenario
since no scripts depend on GA in the way jQuery UI depends on jQuery.
`async` is also not supported by the Android 2.3 browser, but that
browser does have a preload scanner to make up for it.

Once we drop support for IE8 and IE9, the `defer` attribute can be
omitted.

The only downside is that the snippet is not a pure JavaScript
solution anymore, meaning it cannot be moved or concatenated into a
`.js` file. On the other hand, no one seemed to be doing that anyway;
everyone just inlines the snippet into the HTML.

Ref. h5bp#1660 (comment)

Closes h5bp#1696.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.