Custom timing metrics for anti-flicker snippets

Anti-flicker snippets hide the page from the visitor. Here's how to use User Timing to measure when they start and end, and how long page is hidden for.

The anti-flicker snippets used by Google Optimize, Adobe Target, Visual Web Optimizer (VWO) hide the page while they alter the page.

How long the page is hidden for varies from visitor-to-visitor and will depend on things like the number and type of experiments, the visitor's device and network speed, or the cohort the visitor is in.

The snippets below measure how log the page is hidden for and uses the User Timing API to create a mark when the page was hidden, another when it was shown again, and a measure to record how long the page was hidden for.

By default the marks and measures are named:

  • anti-flicker-start
  • anti-flicker-end
  • anti-flicker-duration

These marks and can then be captured by SpeedCurve, and added to your dashboards see Using User Timing for more details.

The measurement snippet should be placed immediately after the anti-flicker snippet, and if the site doesn't use the default anti-flicker each is configurable with the element to watch, the name of the selector or element id to watch for and a prefix for the name of the marks and measure.

Google Optimize

// Google Optimize – measure start, end and duration of anti-flicker 

(function (node, selector, name) {

    performance.mark(name + '-start');
    
    const callback = function (mutationsList, observer) {
        // Use traditional 'for loops' for IE 11 support
        for (const mutation of mutationsList) {
            if (mutation.attributeName === 'class' &&
                !mutation.target.classList.contains(selector) &&
                mutation.oldValue.includes(selector)) {

                    performance.mark(name + '-end');
                    performance.measure(name + '-duration', name + '-start', name + '-end');

                    observer.disconnect();
                    break;
            }
        }
    }
    const observer = new MutationObserver(callback);
    observer.observe(node, { attributes: true, attributeOldValue: true });

})(document.documentElement, 'async-hide', 'anti-flicker');

Adobe Target

// Abobe Target – measure start, end and duration of anti-flicker 

(function (node, id, name) {

    performance.mark(name + '-start');

    const callback = function(mutationsList, observer) {
        // Use traditional 'for loops' for IE 11
        for(const mutation of mutationsList) {
            for(const node of mutation.removedNodes) {
                if(node.nodeName === 'STYLE' && node.id === id) {

                    performance.mark(name + '-end');
                    performance.measure(name + '-duration', name + '-start', name + '-end');
    
                    observer.disconnect();
                    break;
                }
            }
        }
    };
  
    const observer = new MutationObserver(callback);
  
    observer.observe(node, { childList: true });
})(document.head, 'at-body-style', 'anti-flicker');

Visual Web Optimizer

// Visual Web Optimizer – measure start, end and duration of anti-flicker 

(function(node, id, name) {

    performance.mark(name + '-start');
                
    callback = function(mutationsList, observer) {

        // Use traditional 'for loops' for IE 11
        for (mutation of mutationsList) {

            for (node of mutation.removedNodes) {
                if (node.nodeName === 'STYLE' && node.id === id) {
                    performance.mark(name + '-end');
                    performance.measure(name + '-duration', name + '-start', name + '-end');
                }
            }
        }
    };

    observer = new MutationObserver(callback);
    observer.observe(node, { childList: true });

})(document.head, '_vis_opt_path_hides', 'anti-flicker');