Skip to main content
Masking

Masking content avoids content flicker and there are different ways to do this.

James Harber avatar
Written by James Harber
Updated over 6 months ago

What is masking?

In Webtrends Optimize, we refer to masking as the technique by which we hide the page, or parts of the page, until the final content is rendered for users. By doing so, we ensure there is no Content Flickering - where users see the old content, a flash, and then the new content loads in.

Where does masking occur?

We have different places where masking can occur:

  • Before the tag, if an async implementation is used.

  • In the tag config or pre init, while tests are being loaded

  • In the test, if you're waiting on additional things from the page before rendering your changes.

Each of these steps are vital in ensuring there is zero content flickering.

Does content flickering matter?

Yes. We have performed the same experiments with and without content flickering, and seen polar-opposite results. Sometimes these will be in your favour, such as the presentation of offers, sometimes they won't be.

Regardless, when implemented properly on your website, the experiences would not flicker and so it's important that they do not when running experiments.

Before the tag

An alternative implementation approach to adding our script tag as HTML directly to the head of the website is to write it asynchronously with masking.

The approach we take here is:

  • Hide the page

  • Set a safety timeout to remove the masking

  • Write the tag to the page using Javascript asynchronously.

  • Onload and Onerror of the tag, remove the masking

This approach has been used successfully on some request-light websites, and effectively removes Webtrends Optimize from the critical load path.

Read more about this approach at: Hybrid Asynchronous Tagging

In tag masking

24 hour cache

Our tag sits on a CDN and is cached for up to 24 hours to keep Google happy. Please plan your updates in advance.

While you're waiting for your tests to load, you have two core options for masking.

  1. Mask the whole website

  2. Mask on a page-by-page (or section-of-page by section-of-page) basis.

  3. Write your own rules.

Where can I manage in-tag masking?

Go to Tag Configurations, and edit either your Live tag, or a Draft one.

Edit your pre-init script:

You should see script like the following appear, if you search for "whitelist":

Make your changes, and click OK / Save once completed.

1 - Masking the whole website

When modifying your tag, you'll see Display Mode. The options in this dropdown are as follows:

  • None: No masking. This is enabled by default, to keep our footprint light unless we decide otherwise.

  • Display: Applying display: none to the body tag.

  • Visibility: Applying visibility: hidden to the body tag

  • Shift: Applying left: -1000%; posiiton: absolute to the body tag.

  • Overlay: Create a layer that sits above the web page.

We pick from these options based on the website - some are fine to have the body hidden, some aren't.

We can also use the below library, with a global regex (e.g. .*), and your own rules, e.g. body { opacity: 0.0000001 !important }. We often use this technique for sitewide page hide, as this is the most reliable in not breaking website functionality.

Snippet:

// Add things into here for all traffic.
WT.optimizeModule.prototype.whitelist = [
{
URLs: [ /.*/i ],
css: 'body { opacity: 0.00000001 !important; }'
}
];

2 - Masking on a per-page basis

For this, we use the library as below and simply add rules into the array WT.optimizeModule.prototype.whitelist each time you build a new test.

This code, as provided, should go into the pre-init section of your tag to fire at the right time.

//Page Hide 
(function pageHide(){ // Add things into here for all traffic.
WT.optimizeModule.prototype.whitelist = [
{
URLs: [
/[\?&]_wt.testWhitelist=true/i
],
css: 'body { opacity: 0.00000001 !important; }'
},
{
URLs: [
/mysite.com\/basket/i
],
css: 'body { opacity: 0.00000001 !important; }'
},
{
URLs: [
/\/collections\//i
],
css: 'body { opacity: 0.00000001 !important; }'
},
// other rules
];

WT.optimizeModule.prototype.checkWhitelist_CUSTOM=function(){var e,t,o,n=function(e){var o=e||"";return{add:function(e){e.length&&(o+=e+"\n")},output:function(e){var t;if(o.length){if(e&&(t=document.getElementById(e)),t)return!1;(t=document.createElement("style")).setAttribute("type","text/css"),e&&(t.id=e),t.styleSheet?t.styleSheet.cssText=o:t.appendChild(document.createTextNode(o)),document.getElementsByTagName("head")[0].appendChild(t),o=""}},remove:function(e,t){if(!e)return!1;e=(t=t||window.document).getElementById(e);e&&"style"==e.nodeName.toLowerCase()&&e.parentNode.removeChild(e)}}},i=WT.optimizeModule.prototype.whitelist||[];try{for(var r="",a=!1,d=0,s=i.length;d<s;d++){var u,c=i[d],p=c;c.URLs&&c.css&&(p=c.URLs,u=c.css||""),!0===function(e){if(!e)return!1;!1==e instanceof Array&&(e=[e]);for(var t,o=0;t=e[o];o++)if(window.location.href.match(t))return!0;return!1}(p)&&(u&&(r+=u),WT.optimizeModule.prototype.wtConfigObj.s_pageTimeout=5e3,WT.optimizeModule.prototype.wtConfigObj.s_pageDisplayMode="custom",a=!0)}return""!==(WT.obfHide=r)&&(e=(e=r).match(/[\{\}]+/)?e:e+"{ opacity: 0.00001 !important; }",t=new n(e),o="wto-css-capi-"+Math.floor(1e3*Math.random()),WT.addEventHandler("hide_show",function(e){e.params&&(!1===e.params.display&&t.output(o),!0===e.params.display&&t.remove(o))}),setTimeout(function(){t.remove(o)},5100)),a}catch(e){}}; WT.optimizeModule.prototype.checkWhitelist_CUSTOM();

})();

If empty, the array should look like this:

WT.optimizeModule.prototype.whitelist = [];

JS-scoped masking

You can create rules that only take effect if masking based on JS conditions, such as document.referrer, screen sizes, device types, etc.

Consider this block:

{
URLs: [
/mysite.com\/.*/i
],
css: 'body { opacity: 0.00000001 !important; }'
}

The component parts are a URL, which you should still provide, and a CSS string. As this is built with Javascript, it's easy to add conditions to this:

{
URLs: [
/mysite.com\/.*/i
],
css: window.innerWidth < 1000 ? 'body { opacity: 0.00000001 !important; }' : ''
}

The above code would only create the CSS rule if the width of the window is less than 1000px.

As another example, this masks only if users have come from a facebook page:
โ€‹

{
URLs: [
/mysite.com\/.*/i
],
css: document.referrer.match(/facebook/i) ? 'body { opacity: 0.00000001 !important; }' : ''
}

The key points are:

condition ? css_if_successful : empty:strong

Testing whitelist entries

We recommend using Content Overrides in Chrome, or an extension like Resource Override for rapidly testing updates locally (on your machine).

If used, you'll be able to find the whitelist array, add values to it, and test it locally before saving changes in the UI.

In the above example, _wt.testWhitelist can be added to any URL to test how the page would respond with whitelisting enabled. e.g. https://www.webtrends-optimize.com/?_wt.testWhitelist=true

You could add multiple values with different CSS rules for each one, if you'd like to test whole page masking vs. masking a block on the page, for example.

3 - Write your own rules

The Optimize tag is highly configurable by a JavaScript dev. In the case of page hiding, we broadcast the hide_show event which you can hook into with WT.addEventHandler, and create your own page hide rules. So if you'd like to use Opacity, or anything else, you can simply write a hook for the hide_show event (which contains details of whether it's a hide or show) and apply any CSS to the page that you like.

WT.addEventHandler("hide_show", function(e){ console.log(e); });

In test masking

Let's say you want to wait for the page to load before executing your changes, and so you hook into the domready state with jQuery:

jQuery(document).ready(function(){ jQuery('#hero p').text("my new text"); });

Optimize's layer-2 masking will only take effect up until your code is delivered to the page. At this point, it will step away.

This third layer of masking needs to happen while you wait for your conditions to be fulfilled. Given you can code these in any way, it is also on you to decide if/how to mask the page.

Example:

// Mask the page 
WT.helpers.css.add('#hero { opacity: 0 !important; }', 'wt-001-mask');

// Make sure there's a safety redisplay
setTimeout(function(){
// Remove the mask
WT.helpers.css.del('wt-001-mask');
}, 4000);

jQuery(document).ready(function(){
jQuery('#hero p').text("my new text");

// Remove the mask
WT.helpers.css.del('wt-001-mask');
});

How the Optimize Build Framework handles masking

The Optimize Build Framework (OBF) writes styles to the page based on your Config.cssHide value. Once polling conditions are fulfilled and transformations are handled, you'll see a showHide function called that removes this mask.

Thankfully, these are not things you need to pay attention to, although if you wish to override the functionality the code is there for you.

Impact on page load performance

All JavaScript has a detrimental impact on page load performance, but masking plays a critical role in limiting the negative impacts of experimentation.

Core Web Vitals find Cumulative Layout Shift (CLS) to be one of the most important factors for on-page performance. This is where parts of the page jump, fractionally or drastically, during the page load process.

For example, a button slowly nudges lower and lower as more aspects load.

Masking combats this - showing nothing and then the final page at the end. If well-built, your tests should have a near-zero CLS score for this reason.

Infact, we have found masking to be the remedy for websites with poor CLS scores, where it doesn't impact other metrics too much but solves CLS issues.

Other metrics though, such as First Contentful Paint, will be affected by masking. They are less impactful than CLS but will be affected.

The degree to which they're affected depends on page performance, but you can see our requests execute in typically under 150ms (often closer to 30ms).

Did this answer your question?