March 5, 2026
Why experimentation is becoming an operating model for smart organizations
A conversation with Valentin Radu, founder of Omniconvert, on experimentation as an operating model, AI and sustainable digital growth. Read more
We are looking for a data analyst! Check the job posting.
Want to build your own A/B test in JavaScript (JS)? Then go through this roadmap to put your own A/B test live.
A/B testing is an ideal tool for getting more return from your website. And to learn more about your website's visitors.
Before setting up an A/B test, it is important to have carefully thought out what the test will look like and what exactly you want to measure.
For example, it is useful to create a design first, so that possible problems already emerge in the design process. In addition, it is good to determine what needs to be measured during the test; for example, if a call-to-action is modified in the test, it is useful to add an Analytics event to it so that it can be easily measured.
Most A/B testing tools advise users to place their tool snippet as high as possible in the head of the website, because of this it sometimes happens that certain objects are not yet present when your test's script is executed.
For this reason, it is important that the script of the test is structured so that changes are only executed after these elements are present. In the example below, I used two objects, the jQuery object and the Google Analytics object. Check out the functions I use to call them below.
If you are using jQuery then start with this object, so you can be sure that the jQuery code you use in the test will actually work. Here I use an “IIFE” (Immediately Invoked Function Expression) for that keeps calling itself (up to 50 times) if the jQuery object has not yet been found.
When the jQuery object is found, it is assigned to variable ‘$’, as is usual for jQuery, and the start function is executed that contains the changes from the test.
var $, JQIndex = 0;
(function getJQ() {
if (window.jQuery) {
//* If jQuery object is present, assign it to $ and execute the start function.
$ = window.jQuery;
start();
} else if (JQindex {
getJQ();
}, 50);Example of a start function with jQuery:
function start(){
$('.cta').css('background', 'red');
console.log('start');
}It is useful to have a console.log in the script at the beginning to check if the test works.
The Google Analytics object is a bit more complex. Here you need not only the object, but also the correct tracker must be looked up and you use the GA ID.
Because this is a more complex object, I control this in its own function that I call before the start function (so we can use GA in the start function):
(function getJQ() {
if (window.jQuery) {
$ = window.jQuery;
getTracker(); //* Here
start();
}Above is a snippet of the jQuery code.
The getTracker function requires a GA ID. What I generally do is create an object with the GA ID and other data such as: what page the test is running on, for what devices, what the ID and name of the test is, and what the variant is. I place this at the top of the test, in the global scope of the test script (outside of functions such as the start function).
I often call this object ‘data’ and it can look like this:
var data = {
gaid: 'UA-XXXXX-XX', //* Enter the GA ID of the website here
category: 'AB-test', //* GA event category, for AB testing generally "AB testing"
testId: 'A01', //* test ID
devices: 'DTM', //* devices: Desktop, Tablet, Mobile
page: 'Home', //* page where test is executed (Home, PLP, PDP)
name: 'Customize CTA color', //* test name
variant: 'B: Variant', //* A: Control - B: Variant
};The getTracker function is divided as follows:
Example of the getTracker function:
//* Variabelen
var tracker, trackerIndex = 0, GAindex = 0;
function getTracker() {
if (window[window['GoogleAnalyticsObject']] && window[window['GoogleAnalyticsObject']].create) {
//* Als het GA object aanwezig is en het "create" attribuut bezit, haal dan alle trackers op en plaats deze in "allTrackers"
var allTrackers = window[window['GoogleAnalyticsObject']].getAll();
//* Deze "for loop" gaat alle trackers af en vergelijkt het tracking ID van de verschillende trackers met het GA ID van het data object.
for (var i = 0; i < allTrackers.length; i++) {
if (allTrackers[i].get('trackingId') === data.gaid) {
//* Als het tracker ID overeenkomt met het GA ID, dan wordt deze tracker toegewezen aan het variabel "tracker" en wordt de functie trackerSend aangeroepen (deze wordt besproken in het volgende hoofdstuk)
tracker = allTrackers[i];
trackerSend('', 'true'); //* Wordt besproken in het volgende hoofdstuk
return; //* Stop loop als de tracker gevonden is.
} else if (i === allTrackers.length - 1 && !tracker) {
//* Als de tracker niet aanwezig is, probeer het dan tot 50 keer met een interval van 50 ms,
if (trackerIndex !== 50) {
console.warn('Tracker not found, Trying again');
setTimeout(function () {
return getTracker();
}, 50);
trackerIndex++;
} else {
//* Als de tracker na 50 nog niet is gevonden, stuur een error naar de console.
console.error("TRACKER: "".concat(toolConfig.gaid, "" NOT FOUND"));
}
}
}
} else if (GAindex < 50) {
//* Als het GA object niet aanwezig is, probeer het dan tot 50 keer met een interval van 50 ms.
JQindex++;
setTimeout(function () {
return getTracker();
}, 50);
GAindex++;
} else {
//* Als het GA object na 50 nog niet is gevonden, stuur een error naar de console.
console.error('Google Analytics not found!');
}
};If the above went well, then you now have the GA tracker available in your test (in the tracker variable) and you can start sending custom events from the test.
Personally, I use a function that sends the info of the data object along with all events.
It is already in the example of the getTracker code: the ‘trackerSend’ function, which is called when the GA tracker is found.
The function looks like this and should be in the global scope of the test script are placed:
function trackerSend() {
var extra = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var nonInteract = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
extra ? extra = ' : ' + extra : extra;
tracker.send('event', data.category, data.testId + ' - ' + data.devices + ' - ' + data.page + ' - ' + data.name + ' - ' + data.variant + extra, {
nonInteraction: nonInteract
});
};This function can receive parameters. The first is information to send along with the event and the second is a boolean (true - false) to indicate whether it is a nonInteract event is or is not. NonInteract is whether the event is fired by user interaction or not?
Consider, for example:
In the getTracker function, the trackerSend function is already there and looks like this:
trackerSend(“”, true);
This is the generic ‘test is loaded’ event. This has no additional info and the nonInteract attribute is set to true.
The event to GA looks as follows:

If you want to add custom events such as click or scroll events, you can do so in the start function:
function start(){
var $cta = $('.cta');
$cta.css('background', 'red');
$cta.on('click', function() {
trackerSend('CTA clicks!');
});
};In this case, you don't need to pass the boolean of nonInteract because the trackerSend function defaults it to false move. So the second parameter of this function does not need to be filled in if it is an interactive event.
The event to GA looks as follows:

If you participated, your test will now look something like this:
var data = {
gaid: 'UA-XXXXX-XX',
category: 'AB-test',
testId: 'A01',
devices: 'DTM',
page: 'Home',
name: 'Kleur CTA aanpassen',
variant: 'B: Variant',
};
var $, tracker, trackerIndex = 0, GAindex = 0;
function getTracker() {
if (window[window['GoogleAnalyticsObject']] && window[window['GoogleAnalyticsObject']].create) {
var allTrackers = window[window['GoogleAnalyticsObject']].getAll();
for (var i = 0; i < allTrackers.length; i++) {
if (allTrackers[i].get('trackingId') === data.gaid) {
tracker = allTrackers[i];
trackerSend('', true);
return;
} else if (i === allTrackers.length - 1 && !tracker) {
if (trackerIndex !== 50) {
console.warn('Tracker not found, Trying again');
setTimeout(function () {
return getTracker();
}, 50);
trackerIndex++;
} else {
console.error("TRACKER: "".concat(data.gaid, "" NOT FOUND"));
}
}
}
} else if (GAindex < 50) {
GAindex++;
setTimeout(function () {
return getTracker();
}, 50);
} else {
console.error('Google Analytics not found!');
}
};
function trackerSend() {
var extra = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var nonInteract = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
extra ? extra = ' : ' + extra : extra;
tracker.send('event', data.category, data.testId + ' - ' + data.devices + ' - ' + data.page + ' - ' + data.name + ' - ' + data.variant + extra, {
nonInteraction: nonInteract
});
};
function start(){
var $cta = $('.cta');
$cta.css('background', 'red');
$cta.on('click', function() {
trackerSend('CTA klikt!');
});
}
(function getJQ() {
if (window.jQuery) {
$ = window.jQuery;
getTracker();
start();
} else if (JQindex < 50) {
setTimeout(() => {
getJQ();
}, 50);
}
})();What we at Online Dialogue often do now is duplicate this code and remove the changes from the test in it, but leave the custom GA events in place and change the variant in the data object to ‘A: Control’.
Once you've done all this and divided both versions 50-50 in the test tool, you can compare the custom events of the two versions to analyze your A/B test and come up with new insights. But first, test both versions some more....
Use the preview function of your test tool and check that in version B the changes are executed correctly (for both desktop, tablet, mobile). And also check if in variant A and B the events are fired correctly. If this is the case, then the test can be put live!
I hope it all worked out! Is it not working out? Then please contact us.