Control the timing of Optimizely Web Experimentation Event dispatch with holdEvents and sendEvents

  • Updated
This topic describes how to:
  • Learn how to use holdEvents and sendEvents JavaScript APIs to address analytics discrepancies and improve performance
Your snippet must be configured to use the /events endpoint to use the holdEvents and sendEvents API methods. Follow these instructions to identify the logging endpoint used by your snippet and confirm that it is using the /events endpoint before proceeding.

Optimizely Web Experimentation snippets that are configured to use the /events endpoint log batches of events (as opposed to generating one network request per event). These batches are sent once per second for the first ten seconds after snippet activation, and then immediately thereafter. holdEvents and sendEvents work together to control the timing of event logging.  You can use them to better control when the snippet begins sending batches of events. You might want to do this if:

  • You want to improve page load performance by waiting to send events until after the page has finished loading

  • To address data discrepancies, you want to align Optimizely Web Experimentation logging with your analytics platform.

Implement the holdEvents and sendEvents APIs

  1. Call holdEvents before the snippet activates (for example, in Project JavaScript or above the Optimizely Web Experimentation snippet tag). This will prevent the snippet from logging any events until sendEvents is called (although any events triggered will be queued in localStorage):

window.optimizely = window.optimizely || [];
window.optimizely.push({type: "holdEvents"});
  1. Call sendEvents when you want to “release” the events. We recommend adding a listener/timeout to call sendEvents from the same place where holdEvents is called, so you make sure both of them are called appropriately. If you call holdEvents but NOT sendEvents, Optimizely Web Experimentation will not track any data.

window.optimizely = window.optimizely || [];
window.optimizely.push({type: "sendEvents"});

After calling sendEvents, the snippet will be permitted to send batches of queued events at the next polling interval and thereafter. Subsequent batches will be sent once per second until 10 seconds have elapsed since the snippet activated. After that point, individual events will be sent as soon as they are triggered. Any events which have not been sent when a visitor navigates will be held in localStorage and sent on the next page where the snippet is implemented, even if the next pageview occurs hours or days later (for example, at the start of a new session).

Use cases

The two primary use cases for implementing sendEvents and holdEvents each requires a distinct implementation strategy.

Improve page load performance

One way to improve page performance is to reduce the number of requests the browser makes during the critical first few seconds after a page starts loading. This is when users are most likely to notice that a page is loading slowly. 

You can ensure that Optimizely Web Experimentation does not track any events during this period by calling holdEvents before the snippet activates - as described above - and then binding a call to sendEvents to an event fired by the browser when it finishes loading some part of the page.  

For example, you might want to prevent Optimizely Web Experimentation from generating any event requests until after window.load. Your implementation might look something like this:

window.optimizely = window.optimizely || [];
window.optimizely.push({type: "holdEvents"});
window.addEventListener("load", function() {
    window.optimizely.push({type: "sendEvents"});
});

It is up to you to pick the most appropriate event to bind the sendEvents call to. The load event happens after all assets have been downloaded, which is late in the page load process. A more aggressive implementation might use the DOMContentLoaded lifecycle event. With sendEvents, you have the flexibility to align the snippet’s logging behavior to whatever best suits your website’s needs.

Mitigate analytics discrepancies

As discussed in this article on troubleshooting analytics discrepancies, there are several reasons why the numbers you see in Optimizely Web Experimentation may not match what you see in your analytics platform. One common cause of discrepancies is timing.  

Typically, the Optimizely Web Experimentation snippet will be implemented high in the head of your pages. This ensures that Optimizely Web Experimentation can initialize quickly and run variation code to manipulate content without causing page flashing. By contrast, analytics tracking code is frequently implemented to fire later during page load, usually in the body of the document. Analytics libraries are not responsible for manipulating content and therefore flashing isn’t a concern.

This mismatch in timing means that Optimizely Web Experimentation will typically start logging events before your analytics platform does--sometimes several seconds earlier. This leads to Optimizely Web Experimentation counting more conversions and unique visitors than your analytics platform, as visitors who exit a page very quickly may be tracked by Optimizely Web Experimentation but not your analytics platform.

You can address this timing mismatch by calling holdEvents before the snippet activates, and then calling sendEvents from within a callback function provided by your analytics library.

For example, you might want to prevent Optimizely Web Experimentation from generating any event requests until after a Google Analytics tracker object is ready for interaction. You could take advantage of readyCallback to do this. Your implementation might look something like this:

// Implemented above the Optimizely snippet code
    window.optimizely = window.optimizely || [];
    window.optimizely.push({type: "holdEvents"});

// Implemented later on the page
    ga(‘create’, ‘UA-XXXXX-Y’, ‘auto’);
    ga('set', 'anonymizeip', true);
    ga(function(tracker) {
        window.optimizely.push({type: "sendEvents"});
    });
This implementation instructs Optimizely Web Experimentation to send events once the GA tracker object is ready for interaction. However, this does not mean that a GA event has been fired. If you want to align Optimizely Web Experimentation to a GA event that is implemented on every page (a pageview event, for example), you could use hitCallback.

Finally, your implementation will vary depending on the analytics platform you are using. Many platforms provide similar callback functionality:

Mixpanel

  • mixpanel.init(): fired when initializing the Mixpanel library, utilize the loaded callback in the config object

  • mixpanel.track(): fired when tracking an event with Mixpanel, supports an optional callback function

Amplitude

Impact on results

When implementing holdEvents and sendEvents, the Optimizely Web Experimentation snippet may experience a delay before it begins to send events. This will affect Optimizely Web Experimentation results in two ways:

  • Optimizely Web Experimentation will record fewer unique visitors. Visitors who view one page, exit before the first batch of events being sent, and never return to the site will not be tracked.

  • Optimizely Web Experimentation will record fewer conversions. Conversions triggered by visitors who immediately exit before the batch being sent and never return to the site will not be tracked.

The magnitude of these effects will increase with the amount of time the snippet must wait before it begins to send events. This is desirable if your goal is to align Optimizely Web Experimentation's results with your analytics platform.

Visitors who convert and then navigate to a new page where the snippet is implemented (as opposed to exiting the site) will be tracked when their events held in localStorage are picked up and sent. This is true even when the next pageview occurs hours or days later (i.e. at the start of a new session).