Quizzes#
Directive#
- {quiz} [type [arg ...]]#
This directive adds an interactive quiz. The quiz questions are laid out with markup, and user input fields are placed with
quiz-inputandquiz-selectroles. The following quiz types are supported:static(default): A quiz with static answers.table: A table-based quiz with dynamically generated questions.
The button checks the provided answers. It turns green when all the answers are correct, and the quiz fields are locked.
quizdirectives generate<tdoc-quiz>elements.Options
- :class: name [name...] (IDs)#
A space-separated list of CSS classes to add to the outer container.
- :style: property: value; [property: value; ...]#
CSS styles to apply to the outer container, e.g.
max-width: 30rem;.
Types#
Static#
Static quizzes have a fixed set of questions. They can contain arbitrary markup,
interspersed with quiz-input and quiz-select roles to
indicate where the input fields should be placed and what the answers should be.
Table#
Table quizzes ({quiz} table) dynamically generate questions: when a question
is answered correctly, a new question is generated below. The quiz argument after the type is the name of a generator function
registered in quiz.generators.
The directive must contain a table consisting of one or more template rows
containing quiz-ph placeholders for the variable parts of the
question, and quiz-input and quiz-select roles for the
input fields. The text of the placeholder and field roles is an identifier to be
looked up in the object returned by the generator function.
```{role} input(quiz-input)
:style: width: 3rem; text-align: center;
```
```{quiz} table sumProduct
| $a$ | $b$ | $a + b$ | $a \cdot b$ |
| :----------: | :----------: | :----------: | :--------------: |
| {quiz-ph}`a` | {quiz-ph}`b` | {input}`sum` | {input}`product` |
```
Placeholder#
- {quiz-ph}#
This role defines a placeholder for a variable part of a question in a dynamically generated quiz. The text of the role is an identifier to be looked up in the object returned by the generator function.
quiz-phroles generate<tdoc-quiz-ph>elements.
Field#
- {quiz-input}#
This role defines an
<input type="text">field. The role normally isn't used as-is; instead, custom roles are derived from it with theroledirective, which allows controlling the appearance and behavior of the fields.For example, the following block defines an
inputrole that places quiz inputs having a width of3remand aligning their text centered. Theinputrole can then be used inquizdirectives that follow its definition.```{role} input(quiz-input) :style: width: 3rem; text-align: center; ``` ```{quiz} | $a$ | $b$ | $a + b$ | | :-: | :-: | :--------: | | 1 | 2 | {input}`3` | ```Note that custom roles only apply to the document in which they are defined, and can be redefined multiple times in the same document.
Options
See the common field options below.
- {quiz-select}#
This role defines a
<select>field. The role cannot be used as-is; custom roles must be derived from it with theroledirective, to set the selectable options wih:options:, and optionally to control the appearance and behavior of the field.For example, the following block defines a
selectrole that provides two optionsfalseandtrue. Theselectrole can then be used inquizdirectives that follow its definition.```{role} select(quiz-select) :options: | : false : true ``` ```{quiz} | a | b | a or b | a and b | | :--: | :---: | :------------: | :-------------: | | true | false | {select}`true` | {select}`false` | ```Note that custom roles only apply to the document in which they are defined, and can be redefined multiple times in the same document.
Options
See the common field options below.
- :options: option [option ...] (one line per option)#
Defines the selectable options, one option per line. This requires using multi-line options, by starting the value with
|to preserve newlines.
- {quiz-check} [name]#
This directive defines a group of
<input type="radio">or<input type="checkbox">fields, for use in multiple-choice questions. The content of the directive is a bullet list, where each list item is a choice. The correct choice(s) are marked as solutions by prefixing them with:(the prefix is removed). The list items can contain arbitrary markup.Options
See the common field options below (the
checkoption isn't supported).- :multi:#
When set, allow multiple answers in the field group. Uses checkboxes instead of radio buttons.
- :randomize:#
When set, randomize the order of the choices when the page loads.
Common field options#
- :check: name [name ...] (IDs)#
A list of check functions to apply when checking the answer for the field. The functions are applied in order.
- :right: [property: value; ...]#
Moves the field or field group to the right of the question text, by applying a
float: right;style. When using this option, the role or directive must be placed before the question text, otherwise it may be vertically misaligned.Additional styles can optionally be provided as the value of the option, e.g.
width: 10rem;. The valuetrueis the same as providing no value, and the valuefalseis the same as not setting the option (this can be useful indefaults).
- :class: name [name...] (IDs)#
A space-separated list of CSS classes to add to the field or field group.
- :style: property: value; [property: value; ...]#
CSS styles to apply to the field or field group, e.g.
width: 10rem;.
Check function#
A check function checks the user-provided answer for one field. It takes a single argument, an object that contains the following attributes:
role(string): The name of the role that created the field.text(string): The text of the role that created the field.field: The DOM object of the field.answer(string): Initially populated with the answer provided by the user.For radio and checkbox fields, the value is
'1'if the field is selected, or'0'if it isn't.
solution(string): Initially populated with the text of the role.For radio and checkbox fields, the value is
'1'if the field is marked as a solution, or'0'if it isn't.
The check function can modify the existing attributes above, for use by the next check function. It can also set the following attributes:
ok(boolean): Whether the answer matches the solution. Checking terminates once this field is set.hint(string): A hint to display to the user if the answer isn't correct.invalid(string): An error message to display, indicating an error in the check.
Check functions referenced by quiz-input and quiz-select
roles must be given a name by assigning to properties of quiz.checks.
They take a second argument containing the optional parameter from the
reference. The following built-in checks are pre-defined:
default: The default check. Equivalent totrim.split(): Split the solution at instances of the parameter (,by default). This changes the solution to anArrayof strings.trim: Trim whitespace at the beginning and end of the answer and solution.remove(): Remove all instances of the regexp specified as a parameter (\s+by default, i.e. remove all whitespace) from the answer and solution.lowercase: Convert the answer and solution to lowercase.uppercase: Convert the answer and solution to uppercase.json: Parse the solution as JSON, and set the result as the new solution.equal: Compare the answer to the solution. This check is evaluated after all other checks if none of them has set theokorinvalidattributes, so it usually doesn't need to be specified.If the solution is a string,
okis set totrueiff the answer and solution compare equal.If the solution is an
Array,okis set totrueiff the array contains the answer.If the solution is an object, the answer is looked up in the object, and
okis set totrueiff the result istrue. If the result is a string, it is assigned tohint.
The following example re-implements the built-in split check.
<script type="module">
const quiz = await tdoc.import('tdoc/quiz.js');
quiz.checks.split = (args, param = ',') => {
args.solution = args.solution.split(param);
});
</script>
Generator function#
A generator function generates a new question for a dynamically generated quiz.
It must be given a name by assigning it to a property of
quiz.generators, so that it can be referenced from quiz
directives. It takes no arguments and returns a question object with the
following attributes:
One function per
quiz-ph, named after the text of the role. The function receives the emptyHTMLSpanElementcorresponding to the placeholder as an argument, and must modify it to set the variable part (e.g. set itstextContent).One check function per
quiz-inputandquiz-selectrole, named after the text of the role.One check function per
quiz-checkchoice, namedNAME_IDX, whereNAMEis the name of the field group andIDXis the zero-based index of the choice.An optional
equal()function taking a previous question object and returningtrueiff they are equal. If this attribute is present, new questions are compared to previous ones and rejected if they compare equal. This avoids generating duplicates.An optional
historyvalue specifying how far back to check for duplicates when generating a new question.
<script type="module">
const [core, quiz] = await tdoc.import('tdoc/core.js', 'tdoc/quiz.js');
quiz.generators.sumProduct = () => {
const max = 12, va = core.randomInt(1, max), vb = core.randomInt(1, max);
return {
va, vb,
equal(other) { return this.va === other.va && this.vb === other.vb; },
history: max ** 2 / 2,
a(ph) { ph.textContent = `${va}`; },
b(ph) { ph.textContent = `${vb}`; },
sum(args) { args.ok = args.answer.trim() === (va + vb).toString(); },
product(args) { args.ok = args.answer.trim() === (va * vb).toString(); },
};
};
</script>
Field hint#
- {quiz-hint}#
This role defines a hint to display when the answer for a field is wrong. It must be placed immediately after the field role for which it provides a hint, with only whitespace between them.
```{role} input(quiz-input) :right: width: 5rem; ``` ```{quiz} 1. {input}`42`{quiz-hint}`It's a number between 40 and 50.` What is the answer to the ultimate question of life, the universe, and everything? ```
Libraries#
tdoc/quiz.js#
This module
(source)
provides functionality to support quiz directives.
Module globals
- quiz.checks#
An object containing check functions. Custom check functions can be defined by assigning to object attributes.
- quiz.generators#
An object containing generator functions. Custom generator functions can be defined by assigning to object attributes.
Classes
- class quiz.QuizElement()#
The class implementing
<tdoc-quiz>custom elements. It extendsTdocElement.Properties
- QuizElement.quiz#
An instance of a
QuizBasesubclass depending on the type of quiz that implements the quiz logic for the element.