Igor Simic
3 years ago

Google anti-flicker - how to make it better


You have implemented Google anti-flicker snippet code but you can still see some flickering? Here is how you can improve it!

What is Google anti-flicker
Google Anti-flicker snippet is used for Google Optimize, mostly when performing A/B tests. The reason why you need it is next:
Google Optimize uses JavaScript based A/B test, that means that JS code needed to run A/B test must be loaded when page loads and only after that it can perform switching the pages or the content. Of course this can take some time, very short time - counted in milliseconds, but just enough for user to see the not yet hidden content for a brief moment - also known as flicker.
Therefore, Google introduced Google anti-flicker which is the small JS snippet, and you should place in top part of your page, prior to any other code on the page and just after declaration of dataLayer and before Tag Manager (if you are using any). 
This will help a lot to hide that flicker effect.
The snippet looks like this:
<style>.async-hide { opacity: 0 !important} </style>
<script>(function(a,s,y,n,c,h,i,d,e){s.className+=' '+y;h.start=1*new Date;
h.end=i=function(){s.className=s.className.replace(RegExp(' ?'+y),'')};
(a[n]=a[n]||[]).hide=h;setTimeout(function(){i();h.end=null},c);h.timeout=c;
})(window,document.documentElement,'async-hide','dataLayer',4000,
{'GTM-XXXXXX':true});</script>
Explanation about each line, you can find here

Some people even use it inside of Google Tag Manager and still can see good results, but that depends on page design and structure.


So, what is the problem and how it can be fixed
Well, if you know little bit of CSS and JS you can see what the anti-flicker is doing. It is attaching "async-hide" class to HTML element of your page soon as is executed, then waits to load optimize data. If optimize data says we have to redirect to another page or to change something on existing page, anti-flicker keeps the page hidden until all of this is done. If nothing needs to be done, it will show the the page, or if no reply from optimize, after 4 seconds it will show the complete page.
The problem is in a way how the anti-flicker keeps the page hidden, it is using css style: 
opacity: 0 !important
this will make page not visible, except in a case when you have background image set in body tag. The reason is, that css opacity:0 is not applied on background images. So, the user can clearly see background image, while above described processes are waiting to be done - and he can see flicker again.

How to fix it
To fix it we have to add another css style to our anti-flicker, which is to hide any background images as well, that is 
background-size: 0 0;
So our, modified antiflicker will look like this:

<style>.async-hide { opacity: 0 !important; background-size: 0 0;} </style>
<script>(function(a,s,y,n,c,h,i,d,e){s.className+=' '+y;h.start=1*new Date;
h.end=i=function(){s.className=s.className.replace(RegExp(' ?'+y),'')};
(a[n]=a[n]||[]).hide=h;setTimeout(function(){i();h.end=null},c);h.timeout=c;
})(window,document.documentElement,'async-hide','dataLayer',4000,
{'GTM-XXXXXX':true});</script>

Now when we have style for hiding the background, we have to apply this on body tag. As you may remember, anti-flicker is placed on html tag and it can not effect body tag in terms of background image. To fix that we can add this class to body tag. 
To achieve  best performance it is recommended to add "async-hide" class to your body tag "manually" - if possible by adding it to source code of your page. If you can do that, than the next step will be remove that class once when anti-flicker decides to show the content. Therefore we have to modify anti-flicker once more, this time we will add a piece of code which will remove async-hide class from body tag when needed. And once more, our modified anti-flicker will look like this:

<style>.async-hide { opacity: 0 !important; background-size: 0 0;} </style>
<script>(function(a,s,y,n,c,h,i,d,e){s.className+=' '+y;h.start=1*new Date;
h.end=i=function(){s.className=s.className.replace(RegExp(' ?'+y),'');document.body.className = s.className.replace(RegExp(' ?' + y), '')};
(a[n]=a[n]||[]).hide=h;setTimeout(function(){i();h.end=null},c);h.timeout=c;
})(window,document.documentElement,'async-hide','dataLayer',4000,
{'GTM-XXXXXX':true});</script>

This will stop flickering with background images.


What if i do not have access to my page source code?

Well in this case we can try to add those styles to body tag directly, by extending style attribute of anti-flicker like this:
<style>.async-hide { opacity: 0 !important;} body{background-size: 0 0;} </style>
<script>(function(a,s,y,n,c,h,i,d,e){s.className+=' '+y;h.start=1*new Date;
h.end=i=function(){s.className=s.className.replace(RegExp(' ?'+y),'')};
(a[n]=a[n]||[]).hide=h;setTimeout(function(){i();h.end=null},c);h.timeout=c;
})(window,document.documentElement,'async-hide','dataLayer',4000,
{'GTM-XXXXXX':true});</script>
Here we are adding just background-size:0, but if you need to add more styles to fix the flickering you can add it in same way as we added this one.

So, now we have added that style to body tag, but this style needs to be removed once the page is ready, in order to that we can extended original anti-ficker snippet by adding JS way of removing background-size... 
<style>.async-hide { opacity: 0 !important;} body{background-size: 0 0;} </style>
<script>(function(a,s,y,n,c,h,i,d,e){s.className+=' '+y;h.start=1*new Date;
h.end=i=function(){s.className=s.className.replace(RegExp(' ?'+y),'');document.body.style.backgroundSize = 'cover';};
(a[n]=a[n]||[]).hide=h;setTimeout(function(){i();h.end=null},c);h.timeout=c;
})(window,document.documentElement,'async-hide','dataLayer',4000,
{'GTM-XXXXXX':true});</script>


HINT: do not try to add this class or style using document.body.className - because when this snippet is executed document.body is not loaded yet and it can not be added. Also if you try to use timeout for this, it is not reliable solution and sometimes you can still see flickering. You may wonder, why not use some eventListeners, like load for example, answer: it is not working, it takes to long time to get a callback and this is making flicker even worst.