A Javascript library adding distribution builders to your experiments.
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.
You can cite distBuilder using its Digital Object Identifier (DOI).
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.
To add the library to your projects and start using distBuilder, just follow the instructions below.
If you are hosting your experiment on a separate website, here is how to add the script:
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>
$j
alias.
Instead of typing $('#Hello')
, type $j('#Hello')
.
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:
<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>
Qualtrics.SurveyEngine.addOnReady
section.
$j
alias. Instead of typing $('#Hello')
, type $j('#Hello')
.
The library is extremely easy to use: it allows you to add fully functional distribution builders in a few lines of code.
<div id="targetdiv">
to your HTML, and copy the code below between
your two <script>
tags.
<div id="targetdiv">
to the HTML of your question, and copy the
code below in the "Custom Javascript" section. See this link if you have never added Javascript to a Qualtrics question before.
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.
The complete creation of a Distribution Builder object is done in three phases:
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:
nRows
, (default: 10): The number of rows of the distribution builder (i.e. the
maximum number of balls that can be allocated to a certain value).
nBuckets
, (default: 10): The number of buckets (columns) to which balls can be
allocated.
minVal
, (default: 0): The value corresponding to the first bucket (the smallest
value).
maxVal
, (default: 10): The value corresponding to the last bucket (the largest
value)
nBalls
, (default: 10): The total number of balls to allocate.
onTouch
, (default: function () {}
): A JavaScript function that will
be called every time the user clicks a button of the distribution builder.
onChange
, (default: function () {}
): A JavaScript function that will
be called every time the user successfully changes the allocation of balls (i.e. when the add/remove
action is performed on non-filled/non-empty bucket, and when there are still balls available to
allocate).
toggleGridClick
, (default: false
): Allow/disallow participants to click
on the distBuilder to change the allocation of the balls.
addTotals
, (default: false
): Add a row to the bottom of the distBuilder
summarizing how many balls are in each bucket.
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();
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.
target
, required argument: The html id
attribute of the element in
which the distribution builder should be displayed. For best results, this element should be a div with
a fixed width.
order
, (default: ‘grid-labels-buttons’): The order in which the elements of the
Distribution Builder should be rendered. For instance, if you want to labels to appear above the grid,
you should specify ‘labels-grid-buttons’.
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();
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:
labels
, an array of length nBuckets
code>. Supplying this argument will
override the default labels.
prefix
, a string (e.g. ‘$’) which you would like to see prepended to all the labels.
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: '€'
});
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);
After setting up the Distribution Builder, its internal state can be conveniently accessed through three methods:
DistributionBuilder.getDistribution()
returns the current allocation of balls in the
form of an array of length nBuckets. If there are four buckets, and the user has allocated two balls
to the first bucket, one to the third one, and zero to the second and fourth, the method will return
[2, 0, 1, 0]
.
DistributionBuilder.getRemainingBalls()
returns the number of balls that have not been
allocated yet.
DistributionBuilder.isComplete()
returns true
is the user has allocated all
the balls, and false
otherwise.
The two examples below demonstrate how those three methods can be used in your experiments and studies.
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();
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:
button
called “SubmitDistribution” with disabled=true
. When clicked,
this button will for now display "Distribution Validated!".
onChange
argument of the
DistributionBuilder object. This is done by using the value returned by the DistributionBuilder.isComplete()
method.
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({});
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({});
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:
"MyDistributionResult"
, or any other name that you want to use. Leave
the value empty: this is where we are going to store the results.
Qualtrics.SurveyEngine.setEmbeddedData()
function provided by Qualtrics
to store a value in the "MyDistributionResult"
variable. The syntax is: Qualtrics.SurveyEngine.setEmbeddedData("MyDistributionResult",
"TheStringYouWantToStoreHere")
DistributionBuilder.getDistribution()
does not return a string: it returns an array. You
must therefore convert this array into a string first. To do so, simply use DistributionBuilder.getDistribution().join()
.
This will join the elements of the array by commas, and you will be able to store it in Qualtrics.
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)
});
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>
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