Stappenplan: A/B-test opzetten in code (JavaScript)

Stappenplan: A/B-test opzetten in code (JavaScript)

Wil je een eigen A/B-test bouwen in JavaScript (JS)? Doorloop dan dit stappenplan om je eigen A/B-test live te zetten.

A/B-testen is een ideaal middel om meer rendement te halen uit je website. En om meer te leren over de bezoekers van je website.

Stap 1: voorbereiding

Voordat je een A/B-test gaat opzetten is het belangrijk dat goed is uitgedacht hoe de test eruit gaat zien en wat je precies wilt meten.

Zo is het handig om eerst een design te maken, zodat in het designproces al mogelijke problemen naar voren komen. Daarnaast is het goed om te bepalen wat er gemeten moet worden tijdens de test; wordt bijvoorbeeld een call-to-action aangepast in de test, dan is het handig om hier een Analytics event aan toe te voegen zodat deze gemakkelijk te meten is.

Stap 2: opbouw van een A/B-test

De meeste A/B-testtools adviseren gebruikers om hun tool snippet zo hoog mogelijk in de head van de website te plaatsen, hierdoor gebeurt het soms dat bepaalde objecten nog niet aanwezig zijn wanneer het script van je test wordt uitgevoerd.

Om die reden is het belangrijk dat het script van de test zo is opgebouwd dat de veranderingen pas worden uitgevoerd nadat deze elementen aanwezig zijn. In onderstaand voorbeeld heb ik twee objecten gebruikt, het jQuery object en het Google Analytics object. Bekijk hieronder de functies die ik gebruik om ze aan te roepen.

jQuery

Als je gebruik maakt van jQuery dan begin je met dit object, zodat je zeker weet dat de jQuery code die je gebruikt in de test ook daadwerkelijk werkt. Hier gebruik ik een “IIFE” (Immediately Invoked Function Expression) voor die zichzelf blijft oproepen (tot 50 keer) als het jQuery object nog niet gevonden is.

Als het jQuery object gevonden is, dan wordt het toegewezen aan variable ‘$’, zoals gebruikelijk is voor jQuery, en wordt de startfunctie uitgevoerd waar de wijzigingen van de test in staan.

var $, JQIndex = 0;

(function getJQ() {
 if (window.jQuery) {
 //* Als jQuery object aanwezig is, wijs het dan toe aan $ en voer de start functie uit.
   $ = window.jQuery;
   start();
 } else if (JQindex < 50) {
 //* Als het jQuery object niet aanwezig is, probeer het dan tot 50 keer met een interval van 50 ms.
//* Als jQuery niet nodig is voor de test, dan hoeft deze functie niet gebruikt worden.
   JQindex++; //* +1 bij JQIndex
   setTimeout(() => {
     getJQ();
   }, 50);

Voorbeeld van een startfunctie met jQuery:

function start(){
 $('.cta').css('background', 'red');

 console.log('start');
}

Het is handig om in het begin een console.log in het script te hebben om te controleren of de test werkt.

Google Analytics (GA) object

Het Google Analytics object is wat complexer. Hier heb je namelijk niet alleen het object nodig, maar moet ook de correcte tracker opgezocht worden en maak je gebruik van het GA ID.

Omdat dit een complexer object is, regel ik dit in een eigen functie die ik aanroep voor de startfunctie (zodat we in de startfunctie GA kunnen gebruiken):

(function getJQ() {
 if (window.jQuery) {
   $ = window.jQuery;
   getTracker(); //* Here
   start();
 }

Bovenstaand is een snippet van de jQuery code.

Voor de getTracker functie is een GA ID nodig. Wat ik over het algemeen doe is een object maken met het GA ID en andere data zoals: op welke pagina de test draait, voor welke devices, wat de ID en naam van de test is en wat de variant is. Dit plaats ik bovenaan de test, in de global scope van het testscript (buiten functies zoals de startfunctie).

Dit object noem ik vaak ‘data’ en kan er zo uitzien:

var data = {
 gaid: 'UA-XXXXX-XX', //* Vul hier het GA ID in van de website
 category: 'AB-test', //* GA event categorie, bij AB-testen over het              algemeen "AB-testen"
 testId: 'A01', //* test ID
 devices: 'DTM', //* devices: Desktop, Tablet, Mobiel
 page: 'Home', //* pagina waar test wordt uitgevoerd (Home, PLP, PDP)
 name: 'Kleur CTA aanpassen', //* naam test
 variant: 'B: Variant', //* A: Control - B: Variant
};

De getTracker functie is als volgt opgedeeld: 

  • Controleren of het GA object aanwezig is en zo ja, controleren of het object het ‘create’ attribuut bezit (dit om zeker te weten dat het GA object volledig is geladen).
  • Het ophalen van alle trackers.
  • De correcte tracker opslaan door middel van het GA ID (UA-XXXX-XX) in het data object.

Voorbeeld van de getTracker functie:

//* 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!');
 }
};

Stap 3: custom events

Als bovenstaand goed is gegaan, dan heb je nu de GA tracker beschikbaar in je test (in het tracker variabel) en kan je beginnen met het versturen van custom events vanuit de test.

Zelf gebruik ik een functie die de info van het data object meestuurt met alle events.

Deze staat al in het voorbeeld van de getTracker code: de functie ‘trackerSend’, die aangeroepen wordt wanneer de GA tracker gevonden is.

De functie ziet er als volgt uit en moet in de global scope van het testscript geplaatst worden:

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
 });
};

Deze functie kan parameters ontvangen. De eerste is informatie om mee te sturen met het event en de tweede is een boolean (true – false) om aan te gegeven of het een nonInteract event is of niet. NonInteract is de vraag of het event wordt afgevuurd door de interactie van de gebruiker of niet?

Denk bijvoorbeeld aan: 

  • Event versturen bij pagina laden: nonInteract = True, aangezien bij het laden van de pagina er geen gebruikers interactie is.
  • Event versturen na het klikken op CTA: nonInteract = False, aangezien er interactie is van de gebruiker, namelijk de klik op de CTA.

In de getTracker functie staat de trackerSend functie al en ziet er als volgt uit:

trackerSend(“”, true);

Dit is het algemene ‘test is geladen’ event. Dit heeft geen extra info en het nonInteract attribute staat op true.

Het event naar GA ziet er als volgt uit:

Als je custom events wilt toevoegen aan bijvoorbeeld klik of scroll events, dan kan je dit doen in de startfunctie:

function start(){
 var $cta = $('.cta');

 $cta.css('background', 'red');

 $cta.on('click', function() {
   trackerSend('CTA klikt!');
 });
};

In dit geval hoef je de boolean van nonInteract niet door te geven omdat de trackerSend functie deze standaard op false zet. De tweede parameter van deze functie hoeft dus niet ingevuld te worden als het een interactief event is.

Het event naar GA ziet er als volgt uit:

Stap 4: afronden code

Als je mee hebt gedaan, dan zal je test er nu ongeveer zo uitzien: 

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);
 }
})();

Wat we bij Online Dialogue nu vaak nog doen is deze code dupliceren en daarin de wijzigingen van de test verwijderen, maar de custom GA events laten staan en de variant in het data object veranderen naar ‘A: Control’.

Stap 5: testen

Als je dit allemaal hebt gedaan en beide varianten 50-50 hebt verdeeld in de testtool, dan kan je de custom events van de twee versies vergelijken om zo je A/B-test te analyseren en tot nieuwe inzichten te komen. Maar eerst beide versies nog even testen…

Gebruik de preview functie van je testtool en controleer of in versie B de veranderingen goed worden uitgevoerd (voor zowel desktop, tablet, mobiel). En check ook of bij variant A en B de events juist worden afgevuurd. Als dit het geval is, dan kan de test live gezet worden!

Ik hoop dat het allemaal gelukt is! Lukt het niet? Neem dan contact met ons opnemen.

Yuran van den Bosch - developer

Yuran is developer bij Online Dialogue. Er is geen A/B-test die hij niet kan maken ;)