distBuilder

A Javascript library adding distribution builders to your experiments.

1. Introduction

A. Why distBuilder?

In their paper 'Lay understanding of probability distributions' (2014), published in 2014, Daniel Goldstein and David Rothschild have highlighted the benefits of using graphical interfaces called distribution builders to study subjective probabilities, perceptions of frequency, and confidence judgements. This tool was first developed by Sharpe, Goldstein and Blythe (2000), and was featured later in Goldstein, Johnson and Sharpe (2008) and Delavande and Rhowedder (2008). However, the implementation of such distribution builders in online studies is not straightforward, as most survey platforms do not implement this type of question.

The distBuilder library was created to address this issue, and make the implementation of distribution builders easy and accessible to researchers. The library is user-friendly, and requires very little programming knowledge to be used.

B. Citing distBuilder

You can cite distBuilder using its Digital Object Identifier (DOI).

C. About the author

This library is developed by Quentin André, an assistant professor of Marketing at the Leeds School of Business, University of Colorado Boulder. If you have any comment, feedback or suggestion regarding this library or its documentation, please let me know at quentin.andre@colorado.edu or use GitHub issues.

2. Installation

To add the library to your projects and start using distBuilder, just follow the instructions below.

A. Adding to a website

If you are hosting your experiment on a separate website, here is how to add the script:

  1. Download the Library using the button at the top of the page.
  2. Add the Javascript file distributionbuilder.min.js and the CSS file distributionbuilder.css to your html code:

    <link rel="stylesheet" href="distributionbuilder.css">
    <script src="distributionbuilder.min.js"></script>
  3. That's it! You can now add your own distribution builders to your webpage. No need to include jQuery! distBuilder automatically injects it, and makes it accessible under the $j alias. Instead of typing $('#Hello'), type $j('#Hello').

B. Adding to Qualtrics

If you prefer starting from a minimal working example and tweaking it, you can find a fully functional integration to Qualtrics here in the form of a .qsf file, that you can import in Qualtrics.

If you prefer detailed instructions, just follow these simple steps:

  1. Navigate to the "Look and Feel" section of your survey, and click on the "Advanced" tab
  2. Edit the "Header" section, and add the following lines to load the library script and the library styles:
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/QuentinAndre/DistributionBuilder@master/lib/distributionbuilder.css">
    <script src="https://cdn.jsdelivr.net/gh/QuentinAndre/DistributionBuilder@master/lib/distributionbuilder.min.js"></script>
  3. That's it! You can now add your own distribution builders in the "Custom Javascript" section of your Qualtrics questions. Make sure to wrap the code in the Qualtrics.SurveyEngine.addOnReady section.
    You also do not need to include jQuery! distBuilder automatically injects it, and makes it accessible under the $j alias. Instead of typing $('#Hello'), type $j('#Hello').

3. Basic Setup

The library is extremely easy to use: it allows you to add fully functional distribution builders in a few lines of code.

var distbuilder = new DistributionBuilder();
distbuilder.render("targetdiv");
distbuilder.labelize();

In just three lines of code, you have created an interactive (try it!) Distribution Builder in the <div> section called “targetdiv”. It consists of three elements:

Each of these elements can be customized to your liking, both using the Javascript methods wrapped by the library and the CSS. Browse the sections below to discover how you can customize the appearance and functionalities of the distribution builder, access the results of your participants, and implement more complex logics in your studies.

4. Customization

The complete creation of a Distribution Builder object is done in three phases:

A. Initialization

The first step in creating a Distribution Builder is to initialize the object:

myDistBuilder = new DistributionBuilder({})

This function call initializes the internal state of the DistributionBuilder object, with a certain number of parameters that you can specify:

var distbuilder = new DistributionBuilder({
    nRows: 20,
    nBuckets: 20,
    minVal: 0,
    maxVal: 100,
    nBalls: 20,
    onTouch: function () {
        console.log("Distbuilder was touched!")
    },
    onChange: function () {
        console.log("Distbuilder was updated!")
    },
    toggleGridClick: true,
    addTotals: true
    });
distbuilder.render("targetdiv");
distbuilder.labelize();

B. Render

After the DistributionBuilder object is initialized, you must call another method to indicate where you want to see it displayed on the page:

DistributionBuilder.render()

This method requires the argument target, and you can optionally supply the order argument to further tweak the appearance of the Distribution Builder.

Using this method to put the labels at the top of the Distribution Builder:
var distbuilder = new DistributionBuilder({
    nRows: 10,
    nBuckets: 20,
    minVal: 0,
    maxVal: 100,
    nBalls: 10,
    onTouch: function () {
        console.log("Distbuilder was touched!")
    },
    onChange: function () {
        console.log("Distbuilder was updated!")
    }
    });
distbuilder.render("targetdiv", "labels-grid-buttons");
distbuilder.labelize();

C. Labelize

You generally want to add some labels to the buckets of your distribution builder:

DistributionBuilder.labelize()

By default, the Distribution Builder automatically creates evenly spaced labels, using the distance between the minimum and maximum value and the number of buckets using the following code:

step = (maxValue - minValue)/nBuckets;
labels = [minValue + step/2 + step*0, minValue + step/2 + step*1, ..., minValue + step/2 +step*(nBuckets-1)]

Calling the method DistributionBuilder.labelize() without arguments will display those labels. However, you are free to customize the labels using the following arguments:

  1. labels, an array of length nBucketscode>. Supplying this argument will override the default labels.
  2. prefix, a string (e.g. ‘$’) which you would like to see prepended to all the labels.
  3. suffix, a string (e.g. ‘€’) which you would like to see appended to all the labels.

The prefix and suffix arguments are always applied, whether or not you specify custom labels using the label argument.

var distbuilder = new DistributionBuilder({
    nRows: 10,
    nBuckets: 20,
    minVal: 0,
    maxVal: 100,
    nBalls: 10,
    onTouch: function () {
        console.log("Distbuilder was touched!")
    },
    onChange: function () {
        console.log("Distbuilder was updated!")
    }
    });
distbuilder.render();
distbuilder.labelize({
    prefix: '~',
    suffix: '€'
});

5. Changing the Distribution

By default, respondents will start from an empty distribution (i.e. zero balls in all buckets).

However, you might want to specify a different starting distribution, or change the current distribution to a specific value. To do so, you can use the method:

DistributionBuilder.setDistribution(dist)

Here is an example:

var distbuilder = new DistributionBuilder({
    nRows: 10,
    nBuckets: 10,
    minVal: 0,
    maxVal: 100,
    nBalls: 60
    });
distbuilder.render();
distbuilder.labelize(});
var dist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
distbuilder.setDistribution(dist);

6. Reading the Data

After setting up the Distribution Builder, its internal state can be conveniently accessed through three methods:

The two examples below demonstrate how those three methods can be used in your experiments and studies.

A. Number of balls allocated

This can be achieved by creating two <div>s called “BallsLeft” and “BallsAllocated”, and assigning a function updating those <div>s to the onChange argument of the Distribution Builder initialization function.

var n_balls = 10;
$j('#BallsLeft').text("You have " + n_balls + " balls left.");
$j('#BallsAllocated').text("You have allocated " + 0 + " balls.");
var distbuilder = new DistributionBuilder({
    minVal: 0,
    maxVal: 100,
    nRows: 10,
    nBuckets: 20,
    nBalls: n_balls,
    onChange: function () {
        var remainingballs = this.getRemainingBalls();
        var ballsallocated = n_balls - this.getRemainingBalls();
        $j('#BallsLeft').text("You have " + remainingballs + " balls left.");
        $j('#BallsAllocated').text("You have allocated " + ballsallocated + " balls.");
    }
});
distbuilder.render("targetdiv");
distbuilder.labelize();

B. Validating the distribution

You probably want the participants to your studies to allocate all the balls before being able to validate their distribution. Fortunately, it is easy to add a button, and only enable it when all the balls have been allocated:

Together, those two steps are ensuring that the distribution is fully specified before the user can click the button.

var distbuilder = new DistributionBuilder({
    minVal: 0,
    maxVal: 100,
    nRows: 10,
    nBuckets: 20,
    nBalls: 10,
    onChange: function () {
        if (this.isComplete()) {
            $j("#SubmitDistribution").attr("disabled", false)
        } else {
            $j("#SubmitDistribution").attr("disabled", true)
        }
    }
});
$j("#SubmitDistribution").click(function() {alert("Distribution Validated!")});
distbuilder.render("targetdiv");
distbuilder.labelize({});

C. Accessing the distribution

We would now like to see in which buckets the balls have been allocated when the user validates the distribution by clicking on the button.

Building upon the previous example, we can change the message "Distribution Validated!" to display the actual distribution that the user has specified. by using the method DistributionBuilder.getDistribution().

var distbuilder = new DistributionBuilder({
    minVal: 0,
    maxVal: 100,
    nRows: 10,
    nBuckets: 20,
    nBalls: 10,
    onChange: function () {
        if (this.isComplete()) {
            $j("#SubmitDistribution").attr("disabled", false)
        } else {
            $j("#SubmitDistribution").attr("disabled", true)
        }
    }
});
$j("#SubmitDistribution").click(function() {
    var message = "The distribution specified by the user is: " + distbuilder.getDistribution();
    alert(message)
});
distbuilder.render("targetdiv");
distbuilder.labelize({});

D. Storing the data in Qualtrics

You will often want the Distribution Builder to be part of larger survey on Qualtrics, and you will want to store the resulting distribution in a variable. This result can be achieved in the following way:

Adding the code to the previous example:

var distbuilder = new DistributionBuilder({
    minVal: 0,
    maxVal: 100,
    nRows: 10,
    nBuckets: 20,
    nBalls: 10,
    onChange: function () {
        if (this.isComplete()) {
            $j("#SubmitDistribution").attr("disabled", false)
        } else {
            $j("#SubmitDistribution").attr("disabled", true)
        }
    }
});
distbuilder.render("targetdiv");
distbuilder.labelize({});
$j("#SubmitDistribution").click(function () {
    var results = distbuilder.getDistribution().join()
    Qualtrics.SurveyEngine.setEmbeddedData("MyDistributionResult", results);
    var message = 'The function "Qualtrics.SurveyEngine.setEmbeddedData("MyDistributionResult", ';
    message +=  results + ') was called. Your data would have been stored in Qualtrics!';
    alert(message)
});

7. CSS Specification

If you want to customize the appearance of the distribution builder, the full list of classes defined by the stylesheet can be found in the distributionbuilder.css file.

The following CSS tree will help you understand the mapping between the CSS classes and the way the Distribution Builder object is rendered on screen.

<div id="MyContainer" class="distbuilder">
    <div class="grid">
        <div class="distrow row2">
            <div class="cell col0">
                <div class="ball col0"></div> // Present in all "cells"
            </div>
            <div class="cell col1"></div>
            <div class="cell col2"></div>
        </div>
        <div class="distrow row1">
            <div class="cell col0 filled"></div>
            <div class="cell col1"></div>
            <div class="cell col2"></div>
        </div>
        <div class="distrow row0">
            <div class="cell col0 filled"></div>
            <div class="cell col1"></div>
            <div class="cell col2"></div>
        </div>
    </div>
    <div class="buttons">
        <div class="distrow">
            <div class="buttongroup">
                <a class="btn btn-default distbutton glyphicon glyphicon-plus"></a>
                <a class="btn btn-default distbutton glyphicon glyphicon-minus"></a>
            </div>
            <div class="buttongroup">
                <a class="btn btn-default distbutton glyphicon glyphicon-plus"></a>
                <a class="btn btn-default distbutton glyphicon glyphicon-minus"></a>
            </div>
            <div class="buttongroup">
                <a class="btn btn-default distbutton glyphicon glyphicon-plus"></a>
                <a class="btn btn-default distbutton glyphicon glyphicon-minus"></a>
            </div>
        </div>
    </div>
    <div class="labels">
        <div class="distrow">
            <div class="label col0">~1€</div>
            <div class="label col1">~2€</div>
            <div class="label col2">~3€</div>
        </div>
    </div>
    </div class="totals"> // Only present if argument `addTotals` is `true`.
        <div class="distrow">
            <div class="total col0">2</div>
            <div class="total col1">0</div>
            <div class="total col2">0</div>
        </div>
    </div>
</div>

8. About

distBuilder is published under the MIT license. The code is written in Typescript, and is transpiled in legacy code for cross-browsers compatibility. It is developed upon the jQuery library, and uses some elements of Bootstrap for styling.

I am grateful for the financial support of ADL Partner and INSEAD, the comments of Nicholas Reinholtz and Bart de Langhe on previous versions of the Distribution Builder, and Dan Goldstein's references on the history of the distribution builder. I would also like to thank Ignazio Ziano for uncovering a bug when trying to use the library.

This page is hosted on GitHub Pages. Theme by mattgraham