Experience API - not only for LRSes
Experience API (xAPI) is a specification made with e-learning in mind. It is generic in its form. For H5P, xAPI-support means xAPI-statements are generated when users interact with the different content types. E.g. for the Multiple Choice content type, three types of xAPI statement may be generated:
- Attempted (The Multiple Choice question is displayed on screen)
- Interacted (The user has selected an alternative)
- Answered (The user finished the task. Statement contains the result, the user's answer, and the correct answer)
xAPI statements are traditionally sent to an LRS (Learning Record Store). The LRS typically aggregates the data and provides drill down reports, learning analytics etc. But xAPI can be utilized for so much more. Here on h5p.org, for example, we use the statements to send data to Google Analytics. In this way, we can e.g. analyze the percentage of the users that looked at all the slides in a Course Presentation.
In this post, we will experiment with how we may use xAPI to communicate between different elements on a web page. For this we have created the following proof-of-concept page:
Did you visit the link above? If so, you may have noticed that each lesson displays an H5P. To be able to get the score from each lesson, we have added a custom JavaScript that among other things listens for statements sent. These statements are used when updating the total score.
Code walkthrough
The proof-of-concept is developed as a Drupal module, and consists mainly of JavaScript and CSS. The Drupal-specific code is only about including these files. The DOM structure is made manually using the Drupal wysiwyg and utilizing the shortcode support. Here is an example which contains one lesson:
<div class="h5p-lessons-result"> <div class="h5p-lessons-progress">Progress</div> <div class="h5p-lessons-score">Score</div> </div> <div class="h5p-lessons" data-total-score="13"> <div class="h5p-lesson active" data-h5p-content-id="16559"> <div class="h5p-lesson-heading">Lesson 1</div> <div class="h5p-lesson-intro">Basic intro to H5P</div> <button class="h5p-lesson-run">Begin</button> </div> </div> <div class="h5p-lesson-content"> <div class="h5p-lesson-h5p" data-h5p-content-id="16559"> <div class="h5p-lesson-h5p-heading">Lesson 1<a href="javascript:void(0)" class="quit-lesson">Quit lesson</a></div> H5P shortcode here </div> </div>
The page has three parts:
- The results (progress and score): .h5p-lessons-result
- The lessons overview: .h5p-lessons
- The actual lessons including the H5Ps: .h5p-lesson-content
The H5Ps itself are initially hidden by using CSS.
The JavaScript code starts off by finding all H5Ps on the current page as seen below:
var numLessons = 0; // Iterate all lessons: $('.h5p-lesson-run').each(function () { var $self = $(this); var $lessonBox = $self.parent(); numLessons++; // Get the H5P contentId var cid = $lessonBox.data('h5p-content-id'); // Find the H5P class instance, by providing the content ID: getInstance(cid, function (instance) { // Create a new Lesson instance var lesson = new XAPIDemo.Lesson($lessonBox, $lessonOverlay, instance); // Handling clicks on the begin lesson button $self.click(function () { if (!$lessonBox.hasClass('locked')) { lesson.show(); } }); // Handle lesson beeing finished // If score is available, it is found in event.data lesson.on('finished', function (event) { self.progressWidget.increment(); if (event.data) { scoreWidget.increment(event.data); } // Enable the next lession enableNextLesson(); }); }); });
Below is the Lesson class, which mainly handles showing/hiding the lesson and acting on xAPI statements.
XAPIDemo.Lesson = (function () { function Lesson($lessonBox, $lessonOverlay, instance) { var self = this; var contentId = $lessonBox.data('h5p-content-id'); var $lesson = $('.h5p-lesson-h5p[data-h5p-content-id=' + contentId + ']'); $lesson.attr('role', 'dialog'); var $heading = $lesson.find('.h5p-lesson-h5p-heading'); $heading.attr('tabindex', 0); // Lesson inherits the EventDispatcher class H5P.EventDispatcher.call(self); // Handles xAPI statements var handleXapi = function (event) { var stmt = event.data.statement; var isParent = (stmt.context.contextActivities.parent === undefined); if (isParent && stmt.result !== undefined && stmt.result.completion === true) { setTimeout(function () { self.hide(event.getScore()); }, 2500); } }; // Display H5P in overlay self.show = function () { $lesson.addClass('open'); $lessonOverlay.addClass('open'); $('.quit-lesson', $lesson).bind('click', function () { var confirmDialog = new H5P.ConfirmationDialog({headerText: 'Are you sure?', dialogText: 'If quiting this lesson, no score will be given.'}); confirmDialog.appendTo($lesson.get(0)); confirmDialog.on('confirmed', function () { self.hide(); }); confirmDialog.show(); }); // Listen to xAPI events! instance.on('xAPI', handleXapi); if (instance) { instance.trigger('resize'); } $heading.focus(); }; // Hide the H5P self.hide = function (scoring) { $lesson.toggleClass('open'); $lessonOverlay.toggleClass('open'); instance.off('xAPI'); $('.quit-lesson', $lesson).unbind('click'); $lessonBox.addClass('done'); self.trigger('finished', scoring); }; } // Lesson inherits the EventDispatcher class Lesson.prototype = Object.create(H5P.EventDispatcher.prototype); Lesson.prototype.constructor = Lesson; return Lesson; })();
Worth to mention here is the handling of xAPI statement (the handleXapi function), where we have to check for two things to be sure the H5P is completed:
- The statement's contextActivities has no parent. I.e if a statement is sent from a question inside a Question Set, that doesn't mean the Question Set is completed. When Question Set itself says it is completed we know all questions inside it are completed. In this case, the statement won't have any parent.
- The statement.result.completion must be true (i.e. use this instead of using the type of verb for the statement)
There are also JavaScript code for creating the score and progress widget. If you would like to get the complete picture, please look at the JavaScript file here and the CSS here
For further readings about xAPI and H5P, have a look here.
Please leave a comment below if you have any other bright ideas on how to utilize xAPI! Also leave a comment if you would like to see an H5P version of the demo.
Comments
khoder
Fri, 07/01/2016 - 11:35
Permalink
Very good.
Thank you!
nadavkav
Fri, 07/01/2016 - 13:10
Permalink
Suport cool
I am on my way to see if I can make it work on piwik too.
nadavkav
Fri, 07/01/2016 - 13:11
Permalink
I meant "Super cool"
.
falcon
Fri, 07/01/2016 - 14:47
Permalink
cool!
cool!