Show assessment question hints on demand.
Introduction
As with distractor rationale, it’s a breeze to add hints to an assessment using Learnosity APIs. This tutorial is based on Tutorial 202: Displaying Distractor Rationale, and will use the same approach to display hints that have been added to Question data using the Learnosity Author site or embedded Question Editor.
Hints are entered in the metadata section of the Author site or Question Editor. There is more than one school of thought about when and how hints should be shown to a student. As such, the last mile of displaying optional information such as hints (or the aforementioned distractor rationale) is up to you. You can store hints in Question data, and Learnosity provides the code to retrieve this information at runtime—but without dictating how the hints should be displayed.
In this tutorial, we will use Question-level events and public methods to display a single hint when a user clicks a button. It’s possible to place multiple hints into the metadata hint property (the Worked Solutions demo shows such an example), or even customize your metadata fields to accommodate a more elaborate hint system. But we’ll start simple here, with a single hint, and you can pick up where we leave off.
Note: This tutorial assumes you are familiar with the Learnosity Items API, PHP, and jQuery.The Request and Host Page
This tutorial uses the same PHP to secure and sign the API request, and the same HTML and CSS for the host page, as the Displaying Distractor Rationale tutorial. We’ve reproduced the code here for your convenience, but if you need additional explanations of any of the following code block, please see Tutorial 202.
Note: This tutorial uses demo values for the consumer key and secret. In production, you must use your own consumer key and secret.
1 <?php
2
3 include_once 'config.php';
4
5 use LearnositySdkRequestInit;
6 use LearnositySdkUtilsUuid;
7
8 $request = [
9 'user_id' => 'student_1234',
10 'session_id' => Uuid::generate(),
11 'items' => ['act1','act2','act3','act4','act5','act6'],
12 'rendering_type' => 'inline',
13 'state' => 'initial',
14 'type' => 'submit_practice',
15 'activity_id' => 'tutorial_activity',
16 'name' => 'Distractor Rationale Example',
18 'config' => [
19 'renderSubmitButton' => true
20 ]
21 ];
22
23 $Init = new Init('items', $security, $consumer_secret, $request);
24 $signedRequest = $Init->generate();
25
26 ?>
27
28 <!DOCTYPE html>
29 <html lang="en">
30 <head>
31 <meta charset="utf-8">
32 <title>Distractor Rationale</title>
33 <style>
34 body {
35 background-color:#F2F2F2;
36 }
37 .learnosity-item {
38 background-color:#FFFFFF;
39 padding:20px;
40 margin:20px 0px;
41 }
42 .distractor {
43 margin-top:5px;
44 padding:10px;
45 background-color:#EBCCD1;
46 color:#b94a48;
47 }
48 </style>
49 </head>
50 <body>
51 <h1>Distractor Rationale Tutorial</h1>
52 <div style="width:750px;">
53 <span class="learnosity-item" data-reference="act1"></span>
54 <span class="learnosity-item" data-reference="act2"></span>
55 <span class="learnosity-item" data-reference="act3"></span>
56 <span class="learnosity-item" data-reference="act4"></span>
57 <span class="learnosity-item" data-reference="act5"></span>
58 <span class="learnosity-item" data-reference="act6"></span>
59 <span class="learnosity-submit-button"></span>
60 </div>
61
62 <!--scripts go here-->
63
64 </body>
65 </html>?>
Initializing the API
When initializing the Items API, we’ll follow the consistent pattern of passing to the init method the signed request created in the PHP block above, as well as an event object. We’ll use the event object to provide a ready listener that will be triggered when the API is loaded and ready to go. The final call will ultimately look like this:
var itemsApp = LearnosityItems.init(<?php echo $signedRequest; ?>, eventOptions);
With the signed request complete, let’s focus on the event object.
Parsing and Setting up The Hint
In the example shown below, we’ll be using jQuery to simplify selectors along the way so, in addition to referencing the Items API in line 63, we’ll also include jQuery in line 62. Our script then begins with an object called responseIDHints
. This will simply associate the appropriate hint with each Question’s response ID.
The eventOptions
object defined in line 68 has only one function, which is the ready listener for Items API. It begins with a loop that walks through all the Questions in the Items API instance. The API provides the Question data and response ID for each Question so we can use both to our advantage. Line 72 retrieves each Question’s hint from the metadata section of the Question data using the getMetadata()
method. It then stores the hint in the aforementioned responseIDHints
object using the Question’s response ID as a key.
Line 74 creates a hint button with Learnosity button style classes, and a data attribute that holds the Question’s response ID. Line 75 appends that data to the DOM element with the same ID. Lines 77 through 79 create a click
event handler for the button that calls the renderHint
function (passing in a reference to the button), which we’ll discuss in a moment.
62 <script src="js/vendor/jquery.min.js"></script>
63 <script src="//items.learnosity.com?[VERSION]"></script>
64 <script>
65
66 var responseIDHints = {};
67
68 var eventOptions = {
69 readyListener: function () {
70
71 $.each(itemsApp.questions(), function(responseID, question) {
72 responseIDHints[responseID] = question.getMetadata().hints;
73
74 var btnHint = '<button type="button" class="btn btn-default btn-sm" data-responseid="' + responseID + '">Hint</button>';
75 $("#" + responseID).append(btnHint);
76
77 $("button").click(function() {
78 renderHint($(this));
79 });
80
81 question.on("changed", function(){
82 $("#" + responseID + "_hint").fadeOut();
83 });
84 });
85
86 }
87 }
Finally, lines 81 through 83 use the Question-level method on()
to bind the "changed"
event to the button. Each time the Question response is changed, the hint will fade out. This gives the end-user the chance to see a hint and then try again after thinking about a new answer.
Next let’s look at how to show the hint when the button is clicked.
Displaying the Hint During an Assessment
To display the hint, we parse the Question’s response ID in line 91, stored earlier in the button’s responseid
data attribute. We then retrieve the corresponding hint from the responseIDHints
object in line 93.
Lines 95 through 100 first check to see if a hint DOM element exists for the relevant Question. If so, line 96 populates the element and fades it in. If not, line 98 creates a div
for this purpose, appends it to the Question, and then fades it in. The div
is given an ID derived from the response ID and “_hint” suffix, and styled with the hint
class.
As the last display step, the renderMath()
method in line 102 nudges MathJax to render any possible LaTeX or MathML that may remain in the page.
90 function renderHint($whichBtn) {
91 var responseID = $whichBtn.data("responseid");
92
93 var hint = responseIDHints[responseID];
94
95 if ($("#" + responseID + "_hint").length) {
96 $("#" + responseID + "_hint").html(hint).fadeIn();
97 } else {
98 var hintDiv = '<div id="' + responseID + '_hint" class="hint">' + hint + '</div>'
99 $("#" + responseID).append(hintDiv).fadeIn();
100 }
101
102 itemsApp.questionsApp().renderMath();
103 }
104
105 var itemsApp = LearnosityItems.init(<?php echo $signedRequest; ?>, eventOptions);
106 </script>
As mentioned previously, we can now send the completed eventOptions
object, along with the signed request object, to initialize the Items API in line 105.
Seeing the Hint in Action
Figure 1, below, shows a Question through all phases of hint interaction. When a student requests the hint, it is displayed in blue at the bottom of the item. Upon entering an answer, the hint is hidden again, and validation is successful.
Figure 1
Displaying the Hint in Item Preview During Authoring
If you've embedded the Author API into your product, you can bind your custom code to the "render:item" event to support rendering during item preview.
Here's an example code snippet that shows showing how you can achieve it, assuming you have already initialized Author API:
authorApp.on('render:item', function () {
const questionsApp = authorApp.questionsApp();
if (!questionsApp) {
return;
}
const questions = questionsApp.questions();
Object.keys(questions).forEach(function (responseId) {
const $question = $('#' + responseId);
const metadata = questions[responseId].getMetadata();
const hintsMetadata = metadata ? metadata.hints : null;
let $button, $hints;
if ($question.length === 0 || !hintsMetadata) {
return;
}
$button = createButton($question, function () {
$hints.toggle();
});
$hints = createHint($question, hintsMetadata);
authorApp.questionsApp().renderMath();
});
});
function createButton($question, handleClick) {
let $button = $question.find('.btn-hints');
if ($button.length === 0) {
$button = $('<button class="btn btn-default btn-sm btn-hints">Toggle hints</button>')
.css('margin', '10px 0 0')
.on('click', handleClick)
.appendTo($question);
}
return $button;
}
function createHint($question, text) {
let $hints = $question.find('.hints');
if ($hints.length === 0) {
$hints = $('<div class="hints">' + text + '</div>')
.css({
display: 'none',
background: 'rgba(0, 0, 255, .1)',
margin: '10px 0 0',
padding: '10px 15px',
borderRadius: '4px'
})
.appendTo($question);
}
return $hints;
}
What you Learned
Building upon the distractor rationale tutorial, you learned how to use Question-level and Author API events and public methods to show hints. Hint data is added to the Question JSON, typically through the Learnosity Author site or your own embedded version of the Question Editor.
Where to Next?
Question-level event and methods let you retrieve any part of Question or response data, giving you lots of flexibility during development. Check out the overview and distractor rationale links in the Additional Resources section.