How DoenetML works
Definitions: Components, Attributes, and Properties
DoenetML is a markup language much like HTML. When creating content in DoenetML, you are essentially working with 3 types of data objects:
-
Components - also called “tags” informally, describe the type of content that is being defined or rendered. Typically, a component will consist of an opening tag
<componentName>and a closing tag</componentName>, with content, content specifications, or other components occuring between these two tags. Some components, if they do not require additional information, may be self-closing,<componentName/>. -
Attributes - provide information necessary to define the component’s desired behavior within the opening tag. The syntax for specifying an attribute is:
<componentName attr="value">. -
Properties (or Props) - props are stored information relating to a component that may be accessed by referencing the component by name, followed by the name of the property.
Example: Illustration of components, attributes and properties
The available attributes and properties vary by component. See the Component Examples section for a full listing for each.
Component Types
The components in DoeneML can be categorized into several functional types that characterize their most common use-case and functionality. The system presented here is not a formal, programmatic characterization; it is intended to mainly help authors of DoenetML find and select useful components when building activities, and to make learning components easier.
To see a list of components organized by type, see the Index by Component Type page.
The different types are:
| Type of Component | Description |
|---|---|
| Paragraph Markup | used within a paragraph, or <p> tag to change the way text is rendered |
| Sectional | used to structure content on the page; may have auto-numbering |
| Input | used to gather user-input, such as text-boxes, multiple-choice selections, or checkboxes |
| Graphical | used to render interactive elements on a graph |
| Display Math | used to render LaTeX; does not invoke the CAS |
| Math | used to define mathematical content; can be symbolic or numerical |
| Math Operator | used to perform mathematical operations |
| General Operator | used to perform operations on math and non-math components |
| Logic | used to define boolean conditions |
| Evaluation | used with answer validation and awarding of credit |
| Text | any component that uses text strings outside the context of a paragraph |
| Subject-specific | components that are unique to a specific STEM field, such as chemistry or physics |
Some components may belong to more than one functional type, such as a <sequence>, which can generate either a numerical or a text sequence, or a <mathInput> which collects student’s mathematical responses and also stores the value as a Math Component for use in computations by the CAS.
The Computer Algebra System (CAS)
DoenetML is linked to a CAS which performs the basic computations necessary for the behavior of mathematical and graphical components, as well as answer-checking.
All Math Components and Graphical Components have access to the CAS, which can be used when evaluating or responding to user interactions with these components. For example, when a user drags a <line>, dilates a <circle>, or updates the coefficient in an equation via a <mathInput>, the CAS instantaneously updates all of the stored property data relating to that component.
Mathematical Display components, by contrast, do not access the CAS.
Basic Language syntax
Components can communicate information between one another by means of references. To create a reference, attach a name attribute to a defined component following the DoenetML naming convention.
Naming conventions
DoenetML naming convention:
- Can only contain numbers, letters, and underscores
- Cannot start with an underscore or a number
- Is case-sensitive
When you need to access the value of the named component or one of its stored properties, use the notation $componentName to create a reference to the component itself, and use the notation $componentName.propertyName to create a reference to one of its properties.
Example: Referencing the stored value of a named component
Sizes: width and height
Several components let you set their rendered size with a width attribute and,
where a component has a height of its own to set, a height attribute:
<textInput/>, <codeEditor/>, <spreadsheet/>,
<tabular/>, <slider/>, <sideBySide/>, <sbsGroup/>,
<graph/>, <image/>, and <video/>. They all read the value
the same way, with two exceptions noted at the end of this section.
A bare number means pixels. width="600" and width="600px" are the same
size. You can also write the size in physical units:
| Value | Meaning |
|---|---|
600 | 600 pixels |
600px | 600 pixels; the unit may also be spelled pixel or pixels, with or without a space before it |
6in | 6 inches, or 576 pixels; also spelled inch or inches |
450pt | 450 points, or 600 pixels |
15cm | 15 centimeters, or about 567 pixels; also spelled centimeter or centimeters |
150mm | 150 millimeters, the same size; also spelled millimeter or millimeters |
A height takes one of these sizes. A width takes them too, and may instead
be written as a percentage.
Relative sizes
A percentage is measured against the width available to the component — the width of the page column it sits in, or of whatever encloses it. It is the one form that keeps a component in proportion when the reader’s window is narrow, so reach for it when a fixed number of pixels would be too wide on a phone:
An expanded <textInput/> takes a percentage the same way, and is 100%
wide unless you say otherwise. A word-sized input is the exception: it keeps
whatever width it is given, and the line it sits in closes around it, so a
percentage there would be a share of a box whose own width depends on the input.
Give a word-sized input an absolute width.
em is not a font-relative size here. Unlike in CSS, DoenetML reads em as
a multiple of 100%, so width="2em" means 200% and width="1em" means 100%.
Write the percentage you mean instead.
A unit DoenetML does not recognize is quietly read as pixels: width="300 furlongs" renders 300 pixels wide. A value that does not begin with a number at
all — width="wide" — yields no size. That is not the same as leaving the
attribute off: the component does not fall back to its default width, it is laid
out with no width of its own. Delete the attribute rather than give it a value
DoenetML cannot read.
Sizes that snap to a preset
<graph/>, <image/>, and <video/> are the first exception.
Their width does not set an exact width; it picks the nearest of five
named sizes, which you can also set directly with their size attribute:
size | Width |
|---|---|
tiny | 70 pixels |
small | 255 pixels |
medium | 425 pixels |
large | 595 pixels |
full | 850 pixels |
So width="400px" and width="50%" both render one of these components at
medium, 425 pixels wide. Reading back the component’s size property tells
you which preset a width landed on.
A percentage means something different here than it does elsewhere. It is a
share of the 850 pixels of the full preset — the widest any component may be —
rather than of the space around the component, so width="50%" picks medium
however narrow the surrounding column happens to be.
An <image/> drawn on a <graph/> is the exception to the exception:
there its width is used as given, measured against the graph’s own scale
rather than snapped to a preset.
Sizes that must be relative
<sideBySide/> and <sbsGroup/> are the second exception. They
divide a row into shares, so their width, widths, and margins are read as
percentages only. An absolute size there raises a warning and is treated as the
percentage of the same number, which is rarely what you meant — write
width="50%", not width="50".