Creating Percentage Bar question type


At Learnosity, we have some pretty creative clients. The need for them to be able to create their own question types is increasing. We want to give third party developers the ability to create their own custom question while utilising the power of the Learnosity Platform for Authoring, Assessment and Reporting. This is exactly what the recently released Custom question is for. With the Custom question, developers have full control over the look and feel of the question, the user interaction and the scoring of the responses.

The Custom question is designed to be very simple to extend. To test drive it, I was asked to follow our knowledge base article creating a percentage bar question, where the student is able to drag the slider on the bar to change the input value.

16904417729603.vkPJXcUSJaLfjeqKtBgY_height640

View Demo

A Custom question is defined by passing a JSON object to the Questions API, just like any other other question type. I figured I could use HTML5’s range input to do this, and style its Shadow DOM using CSS to get the user interface I wanted. In the JSON object, I passed in my custom attributes:

  • js – URL to the question’s JavaScript file
  • css – URL to the question’s CSS file
  • prepend_unit – unit that gets prepended to the value
  • append_unit – unit that gets appended to the value
  • step – the value between incremental points along the slider range
  • min_value – minimum value
  • max_value – maximum value
  • min_percentage – minimum percentage
  • max_percentage – maximum percentage
  • init_value – value of the question when it’s first loaded
  • color – color of the bar
  • valid_response – valid response
  • score – the score students get if the response is valid
{
    "response_id": "custom-range-slider-response-1",
    "type": "custom",
    "js": "//docs.learnosity.com/demos/products/questionsapi/questiontypes/custom_percentagebar.js",
    "css": "//docs.learnosity.com/demos/products/questionsapi/questiontypes/custom_percentagebar.css",
    "prepend_unit": "$",
    "append_unit": "",
    "min_value": "0",
    "max_value": "150",
    "step": "10",
    "min_percentage": 0,
    "max_percentage": 100,
    "init_value": "20",
    "bar_color": "#80D3B4",
    "valid_response": "120",
    "score": 1,
    "stimulus": "If Luke has $150 and he spends $30 on beer, how much money has he got left?"
}

I passed in the stimulus attribute as well, which is available in all question types.

After that, I created a JavaScript module with the init function of the question, where I specified the markup of the question to be rendered. Once this is completed, I used the ready event to notify Questions API that my question was ready:

var PercentageBar = function (options) {
    var template = '//My custom question HTML';

    ...

    this.$el = options.$el;
    this.$el.html(template);

    ....

    options.events.trigger('ready');
}

In order for responses to be saved, I needed to let Questions API know when the response to the question had changed. To do this, I created a listener for the input event. For it to work in IE10, I needed a listener for the change event as well. When the events were triggered, I could call the changed event and pass in the current input value:

$bar.on('input change', function() {
    options.events.trigger('changed', $response.val());
});

Next I had to implement the scoring function. I simply needed to define a scorer function and pass in the question object and response data:

function PercentageBarScorer(question, response) {
    this.question = question;
    this.response = response;
}

Then following the structure of the scoring interface as specified in the knowledge base article, I defined methods to determine if the response was correct, what was the score for the current response and the maximum score of the question.

function PercentageBarScorer(question, response) {
    this.question = question;
    this.response = response;
}

PercentageBarScorer.prototype.isValid = function() {
    return this.response === this.question.valid_response;
};

PercentageBarScorer.prototype.score = function() {
    return this.isValid() ? this.maxScore() : 0;
};

PercentageBarScorer.prototype.maxScore = function () {
    return this.question.score || 1;
};

When the validation button was clicked, it triggered Questions API’s public method validateQuestions. In my JavaScript module, I listened for the validate event which would trigger the validate function I created:

var scorer = new PercentageBarScorer(options.question, $response.val());
function validate() {
    if (self.scorer.updateResponse($bar.val()).isValid()) {
        $percentageBarWrapper.addClass('percentage-bar-valid');
    } else {
        $percentageBarWrapper.addClass('percentage-bar-invalid');
    }
}

options.events.on('validate', function() {
    validate();
});

The last step in creating the JavaScript module was to return the its object containing Question and Scorer properties:

return {
    Question: PercentageBar,
    Scorer:   PercentageBarScorer
};

Embedding the question on the page was just like any of our existing core question types. I followed the guide on Questions API documentation and voilà! We have a new percentage bar custom question! Since I was utilising HTML5 range input and Shadow DOM, this will only work in latest Chrome, Firefox, Safari and IE10+.

All this from start to finish took less than 2 days to implement and we think it will open up a lot of doors for developers out there to do some pretty cool things with our APIs.

View Demo


This post was posted in , , by on