Last updated 29 December 2016. Created on 30 April 2016.
Edited by mmowers. Log in to edit this page.

Project Page

Jump to Section

Module Overview

The module provides a UI for creating any number of A/B/multivariate tests, each with any number of visitor experiences to test (aka "variants", e.g. "control" and "treatments"), and any number of conditions to limit the visitor population that is exposed to the test (e.g. Excluding IE8). In the UI, conditions and experiences are each implemented with custom JavaScript snippets, and tests are each configured with a combination of conditions and experiences, with each experience assigned a fraction to determine the fraction of site visitors that should have each experience. See screenshots of the UI for test administration, test creation, condition creation, and experience creation.

After tests are configured and activated in the UI, the module writes the JavaScript to run the tests. When site visitors hit a page, the JavaScript will be executed with this sequence of logic for each test: First, the conditions of the test are evaluated. If the visitor passes all the assigned condition scripts for the test, they will then be randomly assigned an experience for the test based on the fractions entered for each experience, and that experience script will be executed. The assigned experience will also be stored in a cookie with a name that corresponds to the test, and a value that corresponds to the experience, so that visitors maintain consistent experiences on subsequent page hits. See an infographic of this logic here.

Quickstart Instructions

  1. Install as you would normally install a contributed Drupal module.
  2. Go to People -> Permissions (/admin/people/permissions) and confgure Administer A/B Test Configuration and Administer A/B Test Scripts and Settings permissions as needed.
  3. Go to the Settings tab (/admin/config/user-interface/abjs/settings) and configure as needed.
  4. Now create your first test, along with associated conditions and experiences. First, go to the Conditions tab (/admin/config/user-interface/abjs/conditions) and add a new condition, to limit the test to the desired pages and visitor characteristics. See Example Condition Scripts below.
  5. Go to the Experiences tab (/admin/config/user-interface/abjs/experiences) and add one or more new experiences to test. See Example Experience Scripts below.
  6. Go to the Tests tab (/admin/config/user-interface/abjs/tests) and add a new test. Use the condition and experiences you entered, and assign probabilities to each experience. Set Status to Active to activate the test. At this point the test will be fully functional.
  7. To see the test in action, visit a page for which the test should apply to you (based on the conditions you entered). You can see the testing script in <head> by searching for "abjs" in your page's source. Note that this script will load on every non-admin page if there is at least one active test with at least one condition and one experience.
  8. To see the experience you've been assigned (if it's not already obvious), view your cookies. A test cookie's name will be prefixed with abjs_ unless you changed that prefix on the Settings tab (/admin/config/user-interface/abjs/settings). The remainder of the cookie name will be the unique ID for the test (shown on /admin/config/user-interface/abjs/tests), and the value of the cookie will be the unique ID of the experience (also shown on /admin/config/user-interface/abjs/tests) for which you've been assigned. There will be one cookie for each test. You can change the value of a cookie to manually place yourself in a different experience for that test. For Chrome, there is a plugin called EditThisCookie that allows you to view/edit your cookies easily.


This module has no dependencies and creates its own database tables for tests, conditions, and experiences. Ace Code Editor can be used for creating conditions and experiences, but is included directly from CloudFlare CDN rather than the Drupal Ace module.

Viewing Test Results: Analytics Integration Instructions

This module does not provide an interface for viewing test results. However, you can send test data into whatever analytics platform the website is already using (e.g. Google Analytics, Adobe Analytics, or an in-house analytics system), so experiences can be compared over a range of success criteria available in your analytics interface. Because all the test and experience data for abjs tests is stored in the names and values (respectively) of the test cookies, you can access this data directly in javascript, and then you'll just need to figure out how to send that data via javascript to your analytics platform (see pointers below for the main platforms). You may decide to send the data for each test separately, or you may choose to send all tests and experiences in one string. The following function can be used to concatenate all test cookies together, which should share a common prefix on the cookie name (modify abCookieStart to be equal to the cookie prefix you have on /admin/config/user-interface/abjs/settings):

function getTestDataForUser(){
  var returnValue = "";
  var abCookieStart = 'abjs_';
  var abCookieRegExp = new RegExp(abCookieStart+'[^;]*(;|$)','g');
  if (abCookieRegExp.test(document.cookie)) {
    var abCookieMatches = document.cookie.match(abCookieRegExp);
    for(var i = 0; i < abCookieMatches.length; i++){
      var testAndExp = abCookieMatches[i].replace(';','');
      testAndExp = testAndExp.replace(abCookieStart,'');
      returnValue += testAndExp + '|';
  return returnValue;

This function returns a concatenated string of all the test cookies and values in the form of t_[test_id]=e_[experience_id]|t_[test_id]=e_[experience_id]|..., e.g. t_1=e_3|t_5=e_2|t_7=e_2|. This string can then be sent to your analytics platform of choice:

Platform-specific pointers:

Note that the following instructions are merely pointers. Some of this information may be outdated, incorrect, incomplete, or otherwise less-than-ideal. It's always best to base your implementation directly off the platform documentation.

  • Google Analytics with analytics.js: You can make use of one of your 20 available custom dimensions, and send the testing data as the value of that dimension for pageviews or events at will. First, you'll need to configure the dimension on google analytics. Then, to send testing data on pageviews, you can replace your pageview tracking request (ga('send', 'pageview');) with something that looks like this:
    var testData = getTestDataForUser();
    ga('send', 'pageview' {
      'dimension1': testData

    (Make sure this script is loaded after the testing script, and that you've also included the getTestDataForUser() function.)

    Then, to analyze the data for a specific test, add a Segment to Google Analytics where your dimension contains the test=experience| string of interest, e.g. t_5=e_2|. See this
    for more information.

  • Google Tag Manager (GTM) and Google Analytics:

    See google instructions for setting custom dimensions in GTM. The dimension could be set equal to a variable, where the getTestDataForUser() function above is used as a custom JavaScript variable. See the pointers above for creating segments and viewing the data in google analytics.

  • Adobe Dynamic Tag Manager (DTM) and SiteCatalyst (SC): To add the data to SC via DTM, add a new Data Element in DTM with the script inside the getTestDataForUser() function above. Then, add a Page Rule that fires on Page Bottom which logs the Data Element to a prop and/or evar that you have created in SC. To analyze the test data for a given test in SC, make a new Segment where that prop/evar contains the test=experience| string of interest, e.g. t_5=e_2|.

Alternative Approaches/Modules

This module implements a frontend (JavaScript) A/B testing framework similar to the commercial products Optimizely, Adobe Test&Target, and Visual Website Optimizer, which allows for very simple test implementation for a wide range of tests. However, many frontend frameworks suffer from "flicker", where the original elements on the page will flash briefly before the experience JavaScript modifies them. In fact, persistent flicker issues with Adobe Test&Target was a major reason that this module was developed (see how this module handles test flicker below). Backend (PHP) testing frameworks run the testing logic in PHP (before the html is constructed), so they do not suffer from flicker issues. On the other hand, backend test logic can be much more complicated to implement (especially for a large, multi-faceted system like Drupal), and the use of an html cacheing layer (e.g. Varnish) introduces even more complication because site visitors are typically served cached pages and do not hit the backend. These are the major reasons a frontend framework was chosen for this module. For Drupal backend testing implementations, see A/B test and Multivariate.

Performance and Flicker Control

To mitigate flickering and optimize performance, this module writes the test JavaScript inline into <head> at the top of the page's scripts, so all scripts are run before the page renders. At only ~4k when compressed, the JavaScript should not significantly effect page load speeds. Any experience modifications that do not need to wait on DOM elements within <body> (e.g. CSS changes and JavaScript redirects) can be executed immediately, while modifications that do need to wait on certain DOM elements within <body> can use an event listener DOMContentLoaded. See examples of experience scripts below.

Example Condition Scripts

Global test (all visitors, all pages)

A test with no conditions will run for all visitors on all pages. Alternatively, you can include a condition that simply returns true:

return true;

Restrict test to certain pages

For example, only run test on url pathnames that start with /some-path (case insensitve):

return /^\/some-path/i.test(window.location.pathname);

Restrict test to certain browsers

For example, do not include Internet Explorer 8 or below in the test:

var IE_version = (navigator.userAgent.toLowerCase().indexOf('msie') != -1) ? parseInt(navigator.userAgent.toLowerCase().split('msie')[1]) : false;
return !(IE_version && IE_version < 9);

Example Experience Scripts


A Control experience is an empty script:

//this is empty

CSS modifications

Simply write the desired CSS via document.write().In this example, I'm hiding all elements with a class of some-class:

document.write("<style>.some-class { display:none !important; }</style>");

Alternate pages

To test a completely different version of a page, create the new page (on a new URL path) and perform a redirect. For example, if you want to test a redesign of a page with url of /original-page:

  • Create a new page with a url of, e.g., /alternate-page.
  • Create a condition for the test to only run on /original-page.
  • Create the following redirect experience:
document.write("<style>html { display:none !important; }</style>");

The <html> element is set to display:none to prevent flickering of the original page, since window.location.replace() does not immediately stop rendering of the original page. I am using window.location.replace() instead of window.location = to eliminate the original page from the browser history.

Modify a link location

Because experience and condition scripts are written into <head>, the DOM for the page has not yet been constructed by the time these scripts execute. So any modifications to the DOM need to wait for the DOMContentLoaded event to fire. In this example, I'm finding all links to /original-location and replacing with links to /new-location:

document.addEventListener("DOMContentLoaded", function(event) {

This script assumes we are loading jQuery on the page. Note that the DOMContentLoaded event is not supported by IE8 and below, so you will want to include the condition to exclude IE8 and below. Also note that we don't need to protect against test flicker because the link location is not visible.

Modify text of an element on the page

For example, change the text of an element with an id of some-element:

document.write("<style>#some-element{ visibility:hidden; }</style>");
document.addEventListener("DOMContentLoaded", function(event) { 
  jQuery('#some-element').text('New Text');

In this case, to eliminate flicker of the original text, we first make the button invisible, then on DOMContentLoaded we change the text of the button and make it visible again. See the example above for a discussion of DOMContentLoaded and jQuery.

Arbitrary html replacements:

The same method as just above is used, except .text() is replaced by .html(). For example, if I want to replace the html inside of a section that has an id of some-section with a new html string:

document.write("<style> #some-section{ visibility:hidden; }</style>");
document.addEventListener("DOMContentLoaded", function(event) { 
  jQuery('#some-section').html('<div><p>Example Replacement.</p></div>');

For large html/CSS changes, I open up chrome inspector and make the changes directly in the html on the Elements tab (writing styles inline), then when the modifications are complete, I copy the html to a text editor, replace new lines with spaces, and then copy and paste the new html into the script.

Looking for support? Visit the forums, or join #drupal-support in IRC.


karamveersingh’s picture

Hello All,

I configured A/B JS addon on D7 and using with GTM(Tag manager) but unfortunately page not tracked with event.
I followed complete instructions mentioned on the addon but this instructions->"Then, to analyze the data for a test, add a Segment to Google Analytics where abTestData contains the test=experience| string of interest, e.g. t_5=e_2|." not worked for me.

Is anyone suggest me how to track button event with GTM-Pageview and also tracked on Google analytics.


mmowers’s picture

Please see the issue that was created for this comment.