-
Notifications
You must be signed in to change notification settings - Fork 866
Exercise Markup
Exercises are specified entirely using HTML and are contained within a simple HTML page. You can specify a series of problems (which contain a problem, a question, and a valid solution). Additionally you can specify a series of hints that will be provided to the user upon request.
Exercises are designed to be contained within a single HTML file. The basic layout for a single, simple exercise can be found below.
<!DOCTYPE html>
<html data-require="math">
<head>
<title>Name of Your Exercise</title>
<script src="../khan-exercise.js"></script>
</head>
<body>
<div class="exercise">
<div class="vars">
<!-- Your variables in here... -->
</div>
<div class="problems">
<div>
<p class="problem"><!-- An overview of the problem. --></p>
<p class="question"><!-- The question to ask the student. --></p>
<p class="solution"><!-- The correct answer expected of the student. --></p>
</div>
</div>
<div class="hints">
<!-- Any hints to show to the student. -->
</div>
</div>
</body>
</html>
You can copy this markup into an HTML file and start building an exercise right away.
Depending upon the type of exercise that you're writing you'll likely need to load one or more utility modules in order to render the exercise correctly. This can be done by specifying a data-require="..."
attribute on the HTML element of the page.
There are an ever-increasing number of modules (you can find them in the utils/
directory) but the ones that you'll most-likely need are: math
(for math-related formulas and utility methods) and graph
(for rendering graphs and charts).
Most mathematical problems that you generate will have some bit of random-ness to them (in order to make for an interesting, not-identical, problem). You can generate these values up-front so that you can re-use them again in your problems.
To start you'll want to populate some variables inside the <div class="vars">...</div>
. Variables are typically defined by using a <var></var>
element. You'll want to specify an ID for the var as that'll be the name that you'll refer to in the future.
For example to make a variable named SPEED1
that is a number from 11 to 20 you would do:
<!-- Random number 11 to 20. -->
<var id="SPEED1">11 + rand(9)</var>
Note that you have access to all the properties and methods provided by that JavaScript Math object. The full list of which can be found here:
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Math
Also note that you have full access to all the regular syntax afforded to you by JavaScript, giving you the ability to do something slightly more complicated:
<!-- Random number -10 to -1, 1 to 10. -->
<var id="A">(random() > 0.5 ? -1 : 1) * (rand(9) + 1)</var>
Inside of a <var>...</var>
you can refer to other variables that you've already defined by simply referencing their name. For example in the following we define two variables (AVG
and TIME
) and then multiply them together and store them in a third variable (DIST
).
<!-- Compute one value based upon two others. -->
<var id="AVG">31 + rand(9)</var>
<var id="TIME">1 + rand(9)</var>
<var id="DIST">AVG * TIME</var>
Another common use case is for when you want to pick a random word. To do this you would use an unordered list instead of a var element.
<ul id="VEHICLE1">
<li>bike</li>
<li>bus</li>
<li>camel</li>
<li>elephant</li>
</ul>
This will give you a variable VEHICLE1
that will contain one of the previously-specified words.
At any point in your problems, solutions, or hints you can reference any variable that you've previously defined using the same syntax as before. For example if you wanted to mention a vehicle and speed in your problem you can do:
<p>Alice traveled by <var>VEHICLE1</var> at an average speed of <var>SPEED1</var> miles per hour.</p>
And the variables will be substituted as you would expect them to be.
While displaying single variables can be useful, it's often that you'll want to display entire formulas using mathematical notation. You can define your mathematical notation using LaTeX. The full list of available commands can be found here:
A full book explaining all the commands and how to write Math using LaTeX can be found here:
Inside the Khan Exercise markup we use a <code>...</code>
block to delineate where a formula will go. For example the following code block will be hidden and replaced with a MathJax-rendered formula.
<code>x^2</code>
Naturally you can place <var>...</var>
blocks inside of code blocks, giving you a nicely-rendered final result, for example:
<code><var>A</var>x + <var>B</var></code>
Additionally you can feel free to provide class names on the code block itself. Those classes will be moved to a span and will be wrapped around the final MathJax-generated formula. For example:
<code class="hint_orange"><var>A</var>x</code> <code class="hint_blue">+ <var>B</var></code>
You can feel free to put formulas in your problems, questions, hints, solution, and even multiple choices.
If you want to make sure certain conditions are met when choosing variables, add a data-ensure
attribute to the vars block. For example, if you want to make sure two variables have different values, you can write:
<div class="vars" data-ensure="A !== B">
<var id="A">randRange(1, 10)</var>
<var id="B">randRange(1, 10)</var>
</div>
If the condition in the data-ensure
block evaluates to false, all of the variables will be re-chosen and the data-ensure
will be re-evaluated. Make sure that the condition doesn't fail too often; if it's does, the variables will have to be re-chosen many times, which slows down the browser.
Problems are a collection of a problem overview, a question, and a solution. All problems are contained within an element with a class of "problems".
The basic structure of a problem looks something like this:
<div>
<div class="problem"><!-- An overview of the problem. --></div>
<p class="question"><!-- The question to ask the student. --></p>
<p class="solution"><!-- The correct answer expected of the student. --></p>
</div>
The exact elements that you use aren't important (they could be divs or paragraphs or something else entirely). In the case of the above markup the problem is defined by a div with a class of "problem" (the class name is the important part). You could populate the problem div with a number of paragraphs, or an image, or some other markup.
Same goes for the question markup as well. At the moment the question is appended directly after the problem and is formatted the same, although this may change depending upon the system.
The contents of both the problem and question are completely left at the discretion of the exercise author. Their contents are not handled, or interpreted, in any particular way.
On the other hand, solutions do have a very specific form of markup. Solutions are generally defined with an element that has a class of "solution". For example, a valid solution that is dynamically-generated from a previously-created variable would be:
<p class="solution"><var>round(DIST1)</var></p>
The user would then need to enter a number into the fill-in-the-blank form that matches the DIST1
variable, rounded to the nearest integer.
There are a few answer types available. You can specify what type of answer is expected by using the data-type
attribute on the .solution
element.
-
text (default)
This is the default form. It compares the answer strings literally; no numerical interpretation is done at all (e.g.,
1.0
would not be marked correct if the answer was1
, even though they're numerically equal). The only text transformation is to strip leading and trailing whitespace before comparison. -
radio (default if
ul.choices
present)If you want the user to pick an answer from a list, you can provide the possible choices that you want the user to select from, using an un-ordered list. You'll need to add an UL with a class of
choices
inside of your main problem container.<p class="solution"><code><var>A</var>x <var>BP + B</var></code></p> <ul class="choices"> <li><code><var>-1 * A</var>x <var>BP + B</var></code></li> <li><code><var>A</var>x <var>BN + (-1 * B)</var></code></li> <li><code><var>B</var>x <var>AP + A</var></code></li> <li><code><var>-1 * B</var>x <var>AN + (-1 * A)</var></code></li> </ul>
The above markup will generate a problem that has 5 possible choices to choose from, one of which is the valid solution. Note that when you use multiple choice formatting you can let the user pick from more-complicated answers (such as formulas).
The framework also gives you the ability to provide more possible choices than what may actually be displayed. For example you could provide a total of 5 choices (1 solution + 4 other choices) but only show 3 of them (1 of which will always be the valid answer).
<ul class="choices" data-show="3"> ... </ul>
Additionally the framework provides a mechanism for supplying a "None of the above" choice as a viable option. All you would need to do is supply a
data-none="true"
attribute on your choices UL to enable it, like so:<ul class="choices" data-show="5" data-none="true"> ... </ul>
In this example, 5 possible choices will be displayed - one of which will be the "None of the above" choice. It's possible that the valid solution will be hidden and that "None of the above" will be the correct answer.
-
decimal
Just like text except it compares numerical equality. With
decimal
,1.0
is the same as1
is the same as1
, but no other evaluation is done so fractions and the like don't work.<p class="solution" data-type="decimal">1.0</p>
-
rational
Compares two fractions for equality. Under the hood, it converts both to decimals so the contents of
p.solution
should actually be a decimal version of the correct answer. You can accomplish this usingvar
:<p class="solution" data-type="rational"><var>2/3</var></p>
By default, answers must be reduced to be considered correct. (
4/6
would not be a correct answer.) If you want to specifically allow unsimplified answers, adddata-simplify="optional"
to the element containing the solution. -
multiple
This special type allows you to have a composite of other answer forms as an answer. The "Equation of a line" exercise uses this to ask the user for the slope
m
and y-interceptb
of a line:<div class="solution" data-type="multiple"> <p><code>m</code> = <span class="sol" data-type="rational"><var>M</var></span></p> <p><code>b</code> = <span class="sol" data-type="rational"><var>B</var></span></p> </div>
The code inside the solution will be copied into the solution area and each
.sol
element will be interpreted as if it was its own answer. In this case, you'll get a form that asks form
andb
and gives two separate text entry boxes. Sincedata-type="rational"
was specified on each.sol
element, each text box acts like a single rational answer.Answers are marked correct only if all of the parts are correct. Having a radio input as a part of an answer has not been tested. (You probably don't need more than one radio input, anyway.)
While it's totally possible that you might create an exercise with a single type of problem it's very likely that you'll want to provide students with multiple styles of problems to challenge them. For example one problem could ask for total distance travelled, another could ask for how long it took the travel the distance, etc.
Thankfully you won't have to re-write the entire problem from scratch, you'll only have to write the new portions of the problem that differ from the original. The way you do this is by adding a unique ID to one of your problems and then referencing it from subsequent problems using a data-type="ID"
attribute.
For example in the following markup we create two types of problems. One is the base, core, problem (with the ID of "original") and the other is the problem that inherits from the original.
<div id="original">
<div class="problem">
<p>Alice traveled by <var>VEHICLE1</var> at an average speed of <var>SPEED1</var> miles per hour.</p>
<p>Then, she traveled by <var>VEHICLE2</var> at an average speed of <var>SPEED2</var> miles per hour.</p>
<p>In total, she traveled <var>DIST</var> miles for <var>TIME</var> hour<var>TIME > 1 ? "s" : ""</var>.</p>
</div>
<p class="question">How many miles did Alice travel by <var>VEHICLE1</var>? (Round to the nearest mile.)</p>
<p class="solution"><var>round(DIST1)</var></p>
</div>
<div data-type="original">
<p class="question">How many miles did Alice travel by <var>VEHICLE2</var>? (Round to the nearest mile.)</p>
<p class="solution"><var>round(DIST2)</var></p>
</div>
Note how the second problem doesn't provide a problem definition. This problem definition is inherited directly from the original problem. Any markup provided by a subsequent problem will override the original. For example providing a "question" in a follow-up problem will override the "question" coming from the original.
Using this technique you can easily generate many different styles of problems with only minimal amounts of typing.
If you want to have one problem come up more frequently than another, add a data-weight
attribute to its containing <div>
:
<div id="original" data-weight="3">
...
</div>
<div data-type="original">
...
</div>
In this example, the first problem will appear 3 times as often as the second.
WARNING: This API is very much in flux and is still being defined, use with caution.
It's possible to include graphs in your problems, hints, and solution/answers. A basic graph is defined with a class
of graph
. Width and height are specified as CSS in a style
attribute like so:
<div class="graph" style="width: 200px; height: 200px;">
<!-- Graph-related commands here. -->
</div>
You can then use graph-related commands to do rendering. At the moment the exact API for rendering is very much in flux. We're currently using a variation of the ASCIIsvg API but are looking to switch to something better.
For a current list of commands see the following:
http://www1.chapman.edu/~jipsen/svg/asciisvgcommands.html
If you wish to set the styling for how drawing should look you can currently do this in the data-style
attribute on the graph element. For example:
<div class="graph" style="width: 400px; height: 400px;"
data-style="font-size: 15px; font-family: sans-serif; font-style: bold; stroke: blue;">
</div>
Additionally there are built-in styles of graphs. At the moment the only built-in style is "plane" (which gives you a -10 - 10 x/y coordinate grid). This can be set using the data-graph-type
attribute.
An example full graph definition is as follows:
<div class="graph" style="width: 400px; height: 400px;"
data-style="font-size: 15px; font-family: sans-serif; font-style: bold; stroke: blue;"
data-graph-type="plane">
plot('(' + quadratic + line + ')/' + line);
ASdot([a, limtoa], 4, "black", "white");
</div>
The exercise framework also includes some basic templating support as well. At the moment it only supports conditionals (if/else). You can use them like so:
<p data-if="NUM === 2">The number is 2!</p>
<p data-else>The number is not 2!</p>
Inside the data-if
attribute value you specify the condition that you wish to meet for the associated element to display. The data-else
attribute applies the inverse of the preceding data-if
on the same block level (i.e., the two elements with data-if
and data-else
attributes must be siblings). You can also nest data-if
blocks:
<div data-if="NUM1 === 2">
<div data-if="NUM2 === 2">Both numbers are 2!</div>
<div data-else>The first number is 2, but the second is not.</div>
</div>
A common need of students that are still learning is to have frequent hints that can help to direct them towards a solution. How the hints affect the overall flow of the education should be left up to the framework (for example, in Khan Academy, retrieving a hint will reset your "streak", forcing you to re-do problems that you've done before).
Hints are contained within a <div class="hints"> ... </div>
block. The markup that you use inside the block is completely at your discretion.
<div class="hints">
<p>Remember that <code>d = r * t</code>, or written another way, <code>t = d / r</code></p>
<div>
<p><code>d_<var>V1</var> =</code> distance that Alice traveled by <var>VEHICLE1</var></p>
<p><code>d_<var>V2</var> =</code> distance that Alice traveled by <var>VEHICLE2</var></p>
<p>Total distance: <code class="hint_orange">d_<var>V1</var> + d_<var>V2</var> = <var>DIST</var></code></p>
</div>
<p>Total time: <code class="hint_blue">t_<var>V1</var> + t_<var>V2</var> = <var>TIME</var></code></p>
...
</div>
How the hints are displayed will depend upon the overall framework - but the default that's provided here will make it such that each child of the hints div will be displayed one-at-a-time when the user clicks the "Hint" button. (For example: The "Remember that..." paragraph will be display, then the div with the 3 inner paragraphs, then the "Total time: ..." paragraph, and so on.)
If you wish to provide hints that are specific to the problem that the user is working on (rather than generic to the overall problem). You can do this by providing hints within a problem itself. These hints will override hints in the base "hints" block.
If there are no generic hints, but there are problem-specific hints, the problem-specific hints will be used wholesale.
If there are both generic and problem-specific hints, the class-based inheritance system kicks in. For example, in this particular exercise there is a hint block that contains two placeholders: "hint1" and "hint2". These placeholders do not contain any hints and will be populated later by specific problems.
<div class="hints">
<p>Let's break this problem into smaller and easier pieces.</p>
<p class="hint1"></p>
<p><code><var>A</var> * x = </code><code class="hint_orange"><var>A</var>x</code></p>
<p class="hint2"></p>
<p><code class="hint_orange"><var>A</var>x</code><code class="hint_blue"> + <var>B</var></code></p>
<p>So, the original phrase can be written as <code><var>A</var>x + <var>B</var></code></p>
</div>
Inside a problem the author may write something like the following:
<div data-type="original">
...
<div class="hints">
<p class="hint1">What is <span class="hint_orange">the product of <var>A</var> and x</span>?</p>
<p class="hint2">What is <span class="hint_blue">the sum of <var>B</var></span> and <code class="hint_orange"><var>A</var>x</code>?</p>
</div>
</div>
The framework will take the above markup contained within the <div class="hints"> ... </div>
, go through each of the child elements (in this case, the paragraphs), and replace the associated paragraphs in the main "hints" block (thus "hint1" will replace "hint1", "hint2" will replace "hint2" and so on). What class names you wish to use can be completely at your discretion.