Implementation

<!DOCTYPE html>
<html>
<head>
  <title>Bunchbox implementation sample</title>
  <script src="//cdn.bunchbox.co/4aa7bb5c5616ee6459a19734.min.js"></script>
</head>

<body>
The content of the document......
</body>
</html>

Bunchbox uses a JavaScript file on your site to provide various functions. This file contains all the logic and important information that you set up via the app in your account.


The script is made available via a CDN (Content-Delivery-Network) and thus guarantees a close position to the website visitor and thus an improvement in connectivity.


The script tag for your account can be found in your account settings.

Interface

Global variables

<!DOCTYPE html>
<html>
<head>
  <title>Bunchbox implementation sample</title>
  <script>
    window._bb = window._bb || [];
    window._bb.push([command, value]);
  </script>
  <script src="//cdn.bunchbox.co/4aa7bb5c5616ee6459a19734.min.js"></script>
</head>

<body>
The content of the document......

<script>
  window._bb = window._bb || [];
  window._bb.push([another_command, value]);
</script>
</body>
</html>

Bunchbox avoids defining unnecessary variables on the global namespace. Only the variables _bb_helpers and _bb are defined globally. The latter serves as an interface.


You can use this interface - depending on the command - before, during or after running Bunchbox.


The _bb object defines the push method.


_bb.push(array_with_commands...);


The method expects at least one array describing the command.

User-defined attributes

Visitor attributes

window._bb = window._bb || [];
window._bb.push(
    ['attribute', 'source', 'social'],
    ['attribute', 'hasNewsletter', true]
);

window._bb.push(['attribute', type, [value]])


If you have visitor attributes available, you can pass these Bunchbox before execution. You can use the attributes you provide, for example, for targeting conditions.


E-commerce attributes

window._bb = window._bb || [];
window._bb.push(
    ['ecommerce', 'orderSize', 99.99],
    ['ecommerce', 'productIds', ['id1', 'id2']],
    ['ecommerce', 'orderId', '48b7db13-a8c9-44c1-be18-5ea46e35c01f']
);

window._bb.push(['ecommerce', [type], [value]])


In order to view success metrics such as Revenue-Per-Visitor in the report, you have to give Bunchbox certain values on pages on which you can achieve defined goals. Each conversion can be assigned a purchase value orderSize, the ID of the order orderId and IDs of purchased products productIds.


Manual activation of experiments

window._bb = window._bb || [];
window._bb.push(['activate experiment', '4aa7f5d0a7a7bad33b439b9a']);

window._bb.push(['activate experiment', experimentId, [stepIndex]])


If you have created an experiment that has no targeting conditions, you can activate it manually.


Tracking of event goals

window._bb = window._bb || [];
window._bb.push(['track event goal', '4aa7f5d0a7a7bad33b439b9a', 'goalId']);

window._bb.push(['track event goal', [experimentId], goalId])


If your experiment has an event goal, you can track it with this command. The ID of the experiment is optional. If you omit these, all active experiments are taken into account. If several experiments have an event goal with the corresponding goalId, a conversion is tracked for all applicable targets.


window._bb = window._bb || [];
var link = window._bb.push(['cross domain link']);

// auf einer weiteren Domain

window._bb.push(['cross domain link', link]);

window._bb.push(['cross domain link', [linkObject]])


Bunchbox offers the possibility to serialize existing test participations of a visitor as a link object. You can then manually transfer this link to another domain in order to link both domains unidirectionally. A link has a lifetime of 30 seconds. After receiving a link object on the second domain, you can pass Bunchbox the link.


Safe Mode

window._bb = window._bb || [];
window._bb.push(['safe mode', true]);

window._bb.push(['safe mode', isSafeModeEnabled])


If you want to prevent the execution of variants on certain pages - regardless of targeting - you can ensure this with the command safe mode. This command causes no experiments to be evaluated or executed. There can therefore be no DOM manipulations on the part of the experiments. Experiment targets, on the other hand, will continue to be evaluated.


State request

window._bb = window._bb || [];
window._bb.push([
    'get state',
    function(state) {
        // read from state ...
    }
]);

window._bb.push(['get state', callback])


The get state command can be used to query the state of the current evaluation. This command can be executed at any time. The transferred callback function is executed as soon as the evaluation is completed. The evaluation includes the evaluation of user and tracking filters as well as the experimental targeting conditions, variants and goals. As soon as execution is complete, the specified callback function is executed. The first parameter is a JavaScript object. This object contains the results of the tracking filters and user filters, an overview of all previously defined user attributes and a list of any executed experiments.


{
    accountId: '4b447121b2d3c8851f61a62a',
    userId: '4b447134b2d3c8851f61a62c',
    isUserExcludedByUserFilter: false,
    isUserExcludedByTrackingFilter: false,
    customAttributes: {
        page_type: 'pdp',
        hasNewsletter: false
    },
    activeExperiments: [
        {
            experimentId: '4b44660d0206844801d8f76a',
            experimentName: 'LP Button Test',
            variantId: '4b44660d0206844801d8f783',
            variantName: 'Button Position Left',
            event: 'bb_experiment_lp_button_test_button_position_left'
        }
    ]
}

Prevent flickering

window._bb = window._bb || [];
window._bb.push(['prevent flickering']);

window._bb.push(['prevent flickering', options])


There are some situations where JavaScript runs asynchronously. In these cases, an original variant may be visible before visual changes are made by the actual asynchronously executed variant. This command causes the JavaScript to add a CSS class to the document element. The CSS class is used to set the opacity of the entire page to zero. After the JavaScript is executed, the class is removed again so that there is no effect on the visitor from jumping elements.

Unless otherwise configured, bb-async-hide will be the expected class name. You want to make sure that this class is defined before the Bunchbox JavaScript is executed.

If you already have a class that allows you to set opacity to zero, you can tell us using { className: 'my-custom-hide-class' } as the second parameter.

<!DOCTYPE html>
<html>
  <head>
    <title>Bunchbox implementation sample</title>
    <style>
      .prevent-flickering {
        opacity: 0 !important;
      }
    </style>
    <script>
      window._bb = window._bb || [];
      window._bb.push([
        'prevent flickering',
        { className: 'prevent-flickering' }
      ]);
    </script>
    <script src="//cdn.bunchbox.co/4aa7bb5c5616ee6459a19734.min.js"></script>
  </head>

  <body>
    The content of the document......
  </body>
</html>

Prevent flickering in asynchronous implementation

<!DOCTYPE html>
<html>
  <head>
    <title>Bunchbox implementation sample</title>
    <style>
      .bb-async-hide {
        opacity: 0 !important;
      }
    </style>
    <script>
      /**
       * @param {Window} b The global object.
       * @param {HTMLDocument} e The document.
       * @param {HTMLHtmlElement} c The html element.
       * @param {string} d The name of property of the global Bunchbox object.
       * @param {string} h The account id used to load the Bunchbox script.
       * @param {string} g The class name used to hide the page
       * @param {number} a The maximium timeout in ms used as fallback
       */
      (function(b, e, c, d, h, g, a) {
        b[d] = b[d] || [];
        var f = b[d].show = function() {
          c.className = c.className.replace(RegExp(" ?" + g), "");
          f = b[d].show = function() {};
        };
        b.setTimeout(function() {
          f();
        }, a);
        c.className += " " + g;
        a = e.createElement("script");
        a.async = !0;
        a.src = "//cdn.bunchbox.co/" + h + ".min.js";
        a.onerror = function() {
          f();
        };
        (e.head || e.getElementsByTagName("head")[0]).appendChild(a);
      })(window, document, document.documentElement, "_bb", "539dd741b822cc631f000003", "bb-async-hide", 2000);
    </script>
  </head>
  <body>
    The content of the document......
  </body>
</html>

If you want to load Bunchbox asynchronously, you can do this with this script. The script not only loads Bunchbox, but also prevents potential flickering. Flickering can occur when the default page content is rendered before Bunchbox applies the visual changes of one or more variants to the current web page. To prevent this, this snippet hides the page with a predefined CSS class. The class is added to the document element and removed after successful loading. If Bunchbox resources are blocked by the current browser, the page is immediately displayed again. In addition, the snippet ensures that the page never remains hidden longer than the specified maximum timeout.

Unless otherwise configured, bb-async-hide is the expected class name. You should make sure this class is defined before running the snippet. Also note that 539dd741b822cc631f000003 is an example account ID which you must replace with your account ID.

If you already have a class that allows you to set the opacity to zero, you can tell us by specifying another class instead of 'bb-async-hide'. Similar to the CSS class, the maximum timeout can also be configured.

Variants

The following help functions are available within the context of a code variant.

Redirect

bb.redirect('//www.example.com/');
// oder
bb.redirect('/experiment/variante.html');

bb.redirect(url);


This function delegates the given URL or path to window.location to ensure that no further experiments are performed.


Tracking of event goals

bb.track('event goal', 'engagement');

bb.track(goalType, goalId);


With this function, the event goals defined in the experiment can be tracked. The second parameter goalId corresponds to the ID of the goal defined by you in the experiment.


Waiting for events

bb.waitUntil(
    function() {
        return !!window.$;
    },
    function() {
        $('body').css({ background: 'red' });
    },
    { forever: true }
);

bb.waitUntil(testFn, callback, [options])


Since Bunchbox is executed immediately after loading the page, it is possible that required global objects are not yet available at the time of execution. With bb.waitUntil the following execution can be delayed until the given condition is fulfilled.


Waiting for DOM elements

waitForElements

var task = bb.waitForElements(
    'ul > li',
    function(li) {
        li.textContent = li.textContent + ' New!';
    },
    {
        timeout: 5 * 1000,
        onTimeout: function() {
            alert('Timeout!');
        }
    }
);

if (maybeTrue) task.cancel();

At the time of execution of Bunchbox it is in most cases necessary to wait for required DOM elements to be able to change them. With bb.waitForElements you can wait for one or more elements to change the element as fast as possible without visual side effects for the website visitors.


bb.waitForElements(selector, callback, [options])


exists

bb.exists('h2', function(els) {
    els[0].innerHTML = 'Buy now';
});

bb.exists(
    'ul > li',
    function(els) {
        els.forEach(function(li, i) {
            li.innerHTML = i + 1 + '. Item';
        });
    },
    { forever: true, elements: 3 }
);

bb.exists(selector, callback, [options])


DOM manipulation

append

bb.waitForElements('section', function(section) {
    bb.append(section, '<h1>New category</h1>');
});

bb.append(element, htmlString)


This function allows you to add your own HTML to existing DOM elements.



prepend

bb.waitForElements('section', function(section) {
    bb.prepend(section, '<h1>New category</h1>');
});

bb.prepend(element, htmlString)


This function allows you to add your own HTML code to existing DOM elements. Unlike bb.append, the HTML is placed before the specified element.


replaceWith

bb.waitForElements('section', function(section) {
    bb.replaceWith(section, '<section>New section</section>');
});

bb.replaceWith(element, htmlString)


This function replaces an existing DOM element with new HTML.


css

bb.css('.container h2 { display: none; }');
// oder
bb.css(document.body, '.container h2 { display: none; }');

bb.css([element], styleString)


Adds a stylesheet to the page to design the DOM.


DOM Events

on

bb.on('click', function(event) {
    event.target.innerHTML += '<button>Submit</button>';
});

bb.on('mouseout', 'h2', function(event) {
    event.target.innerHTML = 'Category';
});

bb.waitForElements('h2', function(h2) {
    bb.on('mouseover', h2, function(event) {
        event.target.style.color = 'red';
    });
});

bb.waitForElements('body', function(body) {
    bb.on('click', body, 'h2', function(event) {
        event.target.style.color = 'blue';
    });
});

bb.on(event, [elements, selector], callback)


bb.on offers the appropriate functionality to listen to DOM events. The function can be used to listen to events directly at an element. The event delegation function can also be used.


off

var ids = bb.on('click', 'header', function(event) {
    bb.off(ids);
    event.target.style.backgroundColor = 'red';
});

bb.off(ids)


This function removes one or more event listeners previously registered with bb.on.


Mouse tracking

bb.mousetracking.pause();

// ...

bb.mousetracking.resume();

bb.mousetracking.pause() and bb.mousetracking.resume()


Provided that mouse tracking has been activated for the experiment, tracking for the current variant can be paused or continued using these functions.

Further Information

{
    experimentId: '4aa7e3a5a5ecf992cd77bdf9';
    experimentName: 'AB Checkout Experiment';
    stepId: '4aa7e3a5a5ecf992cd77bdfa';
    variantEvent: 'ab_checkout_experiment_original';
    variantId: '4aa7e3a5a5ecf992cd77bdfb';
    variantName: 'Original';
}

Various information can be retrieved via bb.info. This information can be used, for example, to provide data to third-party tools.


Single-page applications

Single page applications play a special role in AB testing. Unlike traditional pages, there is only an initial page load. All subsequent pages are generated dynamically without additional page load.


The implementation of Bunchbox therefore requires special attention. Bunchbox offers an interface which can be used successfully independent of the framework (e.g. ReactJS, Vue, AngularJS).

Implementation

<!DOCTYPE html>
<html>
<head>
  <title>Bunchbox SPA implementation sample</title>
  <script>
    window._bb = window._bb || [];
    window._bb.push(['auto evaluation', false]);
  </script>
  <script src="//cdn.bunchbox.co/4aa7bb5c5616ee6459a19734.min.js"></script>
</head>
<body>
The content of the document......
</body>
</html>

First, the automatic evaluation must be deactivated, since this is controlled manually in the following.

Controlling the evaluation

var routes = [
    { path: '/a-page', component: Page },
    { path: '/another-page', component: AnotherPage }
];

var router = new VueRouter({
    mode: 'history',
    routes: routes,
    base: '/'
});

_bb = window._bb || [];
router.afterEach(function(current, referrer) {
    _bb.push([
        'evaluate experiments',
        {
            attributes: {
                foo: 'bar'
            }
        }
    ]);
});

This procedure allows Bunchbox to accept the validity of certain attributes and settings. Triggering an evaluation always resets all previously defined data and settings. The newly transferred data determines the new status.

Payload

_bb = window._bb || [];
_bb.push([
    'evaluate experiments',
    {
        attributes: {
            foo: 'bar',
            bar: ['foo']
        },
        ecommerce: {
            orderSize: 99.99,
            orderId: '1e90384530c2',
            productIds: ['0d0cd94dfd74', 'bd79fc089b79']
        },
        safeMode: false
    }
]);

URL fragment based apps

_bb = window._bb || [];
_bb.push(['disable history navigation']);

If the single-page application uses URL fragments (#) to define URLs, this must be communicated to Bunchbox.

The command disable history navigation is used for this purpose.

Variants

Variants for single-page applications often require more attention than their counterparts on conventional websites. Bunchbox only executes variants, but cannot undo code executions. You can listen to a specific event so that a variant can be executed several times in succession and always produces the same result. Bunchbox provides an event emitter which can be used within the variant code. The event emitter can also be used to communicate with other variants.

const ee = bb.getEventEmitter();

const handler = () => console.log('React to custom event');

ee.on('custom:event', handler);

ee.emit('custom:event', 'I am the payload');
ee.off('custom:event', handler);

ee.once('custom:event:2', () => console.log('React to another custom event'));

To find out within the variant code when Bunchbox is evaluated again, you can listen for the event reset. This event is always fired before a subsequent Bunchbox evaluation. The event can thus be used to undo changes made to the variant before it is executed again.


Events can also be triggered by the "outside world". The following command can be used for this purpose.

window._bb = window._bb || [];
window._bb.push(['emit', 'custom:event', 'I am the payload', 'Me too!']);

bb.getEventEmitter();


This function returns an EventEmitter. This EventEmitter provides the functions on, once, off and emit.


ee.on(event, handler);



ee.once(event, handler);



ee.off(event, handler);



ee.emit(event, handler);