Chapter 4. MathML for Publishers
Producing mathematical content (e.g., mathematical expressions, equations, matrices) for the Web has been a longstanding challenge for publishers—especially Science, Technical, and Medical (STM) houses, who make heavy use of math content in their publications. MathML is a markup language specifically designed to surmount the stumbling blocks of formatting math for the Web, and with the release of the HTML5 specification—which formalizes support for embedding MathML in HTML documents—it has been anointed a first-class citizen in Web pages.
This chapter provides a brief introduction to MathML syntax, followed by an example of an MathML-powered interactive equation solver, and a discussion of MathML development tools and cross-browser compatibility.
A Fifteen-Minute Introduction to MathML
MathML is an XML vocabulary, which provides a set of tags expressly designed for annotating mathematical content, just as HTML is fashioned for annotating textual content). Unlike HTML, however, there are actually two distinct flavors of MathML: one called presentation markup and one called content markup. Presentation markup contains a set of elements that are formatting-based and describe the visual properties of mathematical equations and expressions. In contrast, content markup describes expressions by the mathematical operations that are performed within them. As an example of the differences in these two paradigms, let’s take a look at the simple mathematical expression ½.
When defining this expression in “presentational” terms, one would say, “This expression is a fraction with a numerator of one and a denominator of two.” When defining in “content” terms, one would instead say, “This expression performs the operation of dividing the number one by the number two.”
Moving forward in this chapter, we will focus exclusively on MathML presentational markup, for the following reasons:
Presentation markup is specifically geared to address the problem of how to render mathematical expressions, which is what is of primary concern to publishers who are trying to format this content for publication. It enables you to distinguish between the expression ½ and the expression 1 ÷ 2.
Content markup is not widely supported among major Web browsers/ereaders.
If you’re interested in learning more about content markup, see the W3C MathML Content Markup spec for details.
Presentation MathML
Presentation MathML
markup defines a series of elements for tagging the building
blocks of mathematical expressions and equations, including numbers,
operators, fractions, superscripts/subscripts, grouping elements, and
matrices. Each mathematical expression is nested in a root
<math>
tag, just as all Web page content is
nested in a root <html>
tag:
<math
xmlns=
"http://www.w3.org/1998/Math/MathML"
>
Equation content goes here
</math>
In order to ensure that browsers and ereading systems recognize
this content as MathML, a namespace
declaration
(xmlns="http://www.w3.org/1998/Math/MathML"
) must be
added, which specifies that all markup in the
<math>
block is part of the MathML vocabulary
associated with the unique identifier
http://www.w3.org/1998/Math/MathML
.
Here’s an overview of key presentation MathML elements that can be
used in a <math>
element, with some examples of
each:
<mn>
Used to mark up numbers. Per the MathML spec,
<mn>
content is typically rendered in roman font (not italic or slanted) by default:<mn>
18</mn>
<mn>
98.6</mn>
<mn>
99,999,999</mn>
<mn>
should only be used to tag content that is composed of digits and corresponding “punctuation”—e.g., commas and decimal points. It should not be used to tag symbols for constants like π, or to tag fractions.<mi>
Used to tag identifiers in mathematical expressions. In this context, an identifier can be a constant (e.g., π or e), a variable (e.g., x or y), or a mathematical function (e.g., log or sin):
<mi>
e</mi>
<mn>
2</mn>
<mi>
Π</mi>
<mi>
r</mi>
<mo>
Used to tag operators in mathematical expressions, typically symbols that represent operations to be performed (e.g., addition, multiplication, or equals):
<mn>
2</mn>
<mo>
+</mo>
<mn>
2</mn>
<mo>
=</mo>
<mn>
4</mn>
<mi>
log</mi>
<mn>
10</mn>
<mo>
=</mo>
<mn>
1</mn>
While operators are often explicit in mathematical expressions—for example, the + sign in
2 + 2 = 4
—they are often implicit and are not represented by a character in the rendered equation. In the above expression2πr
, there are actually two implicit multiplication operators, as the equation could be written more explicitly as2×π×r
. Best practices in MathML dictate that “invisible operators” should be represented as entities tagged by<mo>
[3]. To represent implicit multiplication operators, use the entity⁢
. Here is a better representation of2×π×r
in MathML<mn>
2</mn>
<mo>
⁢
</mo>
<mi>
Π</mi>
<mo>
⁢
</mo>
<mi>
r</mi>
For implicit function operations (e.g.,
log 10
), use the entity⁡
:<mi>
log</mi>
<mo>
⁡
</mo>
<mn>
10</mn>
<mo>
=</mo>
<mn>
1</mn>
<mrow>
Used to group subexpressions in a larger mathematical expression:
<mrow><mn>
2</mn>
<mo>
⁢
</mo>
<mi>
x</mi></mrow>
<mo>
=</mo>
<mn>
6</mn>
<mfrac>
Used to tag fractions. The
<mfrac>
element expects two “child” elements nested within it: the first element is the numerator of the fraction, and the second is the denominator. The following MathML markup:<mfrac>
<mn>
3</mn>
<mn>
4</mn>
</mfrac>
will render as the fraction ¾.
For situations in which you have more complex expressions in either the numerator or denominator, which encompass more than one MathML tag, use
<mrow>
elements to group the numerator and denominator expressions. For example, the following MathML:<mfrac>
<mrow><mn>
2</mn><mo>
⁢
</mo><mi>
x</mi></mrow>
<mrow><mn>
3</mn><mo>
⁢
</mo><mi>
y</mi></mrow>
</mfrac>
renders as the fraction
<msup>
Used to tag superscripted content in mathematical expressions. Like
<mfrac>
, the<msup>
element expects two child elements nested within it. The first element is the “base” expression, and the second element is the superscripted expression. So the expression x2 is represented in MathML as:<msup>
<mi>
x</mi>
<mn>
2</mn>
</msup>
Warning
Although it may be counterintuitive, the base expression must be included in the
<msup>
element. The following markup is not vaild MathML:<mi>
x</mi><msup><mn>
2</mn></msup>
As with
<mfrac>
, if either the base or the superscript is a complex expression of more than one MathML tag, wrap it in an<mrow>
element. The following markup represents 2x+1:<msup>
<mn>
2</mn>
<mrow><mi>
x</mi>
<mo>
+</mo>
<mn>
1<mn></mrow>
</msup>
<msub>
Used to tag subscripted content in mathematical expressions. The syntax follows the same pattern as
<msup>
.<msub>
must have two child elements: a base expression followed by the subscripted expression:<msub>
<mi>
y</mi>
<mn>
1</mn>
</msub>
The above markup renders as y1
<msqrt>
Used to tag content that should fall within a square root symbol. The following markup will render as
<msqrt>
<msup>
<mi>
x</mi>
<mn>
2</mn>
</msup>
</msqrt>
Putting It All Together: Constructing the Quadratic Formula in MathML
Now that we’ve covered some core fundamentals of MathML markup—numbers, variables, operators, fractions, superscripts/subscripts, and square roots—let’s put this knowledge into practice and construct the quadratic formula.[4] Equation 4-1 shows the standard rendering of the quadratic formula, as seen in most math textbooks:
Example 4-1 shows the quadratic formula constructed in presentation MathML, with annotations explaining each chunk of the markup:
<math
xmlns=
"http://www.w3.org/1998/Math/MathML"
>
<mrow>
<mi>
x</mi>
<mo>
=</mo>
<mfrac>
<mrow>
<mo>
-</mo>
<mi>
b</mi>
<mi>
±
</mi>
<msqrt>
<mrow>
<msup>
<mi>
b</mi>
<mn>
2</mn>
</msup>
<mo>
-</mo>
<mn>
4</mn>
<mo>
⁢
</mo>
<mi>
a</mi>
<mo>
⁢
</mo>
<mi>
c</mi>
</mrow>
</msqrt>
</mrow>
<mrow>
<mn>
2</mn>
<mo>
⁢
</mo>
<mi>
a</mi>
</mrow>
</mfrac>
</mrow>
</math>
Every block of MathML needs to be nested in a root
<math>
element. Note also the namespace declarationxmlns="http://www.w3.org/1998/Math/MathML"
to ensure browsers and ereaders recognize the content as MathML.Here, we’re wrapping the entire quadratic equation in a
<mrow>
element to indicate that all the content within composes a meaningful mathematical unit. This will not have any effect on the equation rendering, but it’s good practice.[5] At the beginning of the<mrow>
, we have the markup<mi>x</mi><mo>=</mo>
, which generates the
on the left side of the equation, tagging x as a variable and the equals sign as an operatorx=
And here’s the start of our fraction, which comprises the entire right side of the formula. The
<mfrac>
block contains two child elements: the first one is for the numerator of the fraction, and the second is for the denominator. Because both the numerator and denominator are complex expressions themselves, we’ll wrap them both in<mrow>
elements.This first
<mrow>
within the<mfrac>
represents the numerator of the fraction on the right side of the quadratic formula. Note that the<mrow>
block encompasses four child elements. The first three are an<mo>
followed by two<mi>
s, which represent the portion of the equation that will render as−b±
. The fourth element is an<msqrt>
, which holds all the content falling under the square-root symbol.The content
±
is a Unicode hex entity that represents the “plus-minus-sign” character.Here’s our square root. Again, because
<msqrt>
falls within the initial<mrow>
within<mfrac>
, all its content will be placed in the numerator of our fraction, along with−b±
.Another
<mrow>
! This<mrow>
groups all the content that’s part of the square root (b2−4ac
). This<mrow>
is not strictly necessary, but again is good practice.The
<msup>
element represents theb2
portion of the square root. As discussed previously, the first child of<msup>
is the base expression, and the second child is the superscripted expression. So here, we’ve indicated that<mi>b</mi>
is the base, and<mn>2</mn>
is the superscript.Note the two occurrences of
⁢
in the4ac
portion of the square root, which explicitly call out the two implicit multiplication operations being performed (4 times a times c).Finally, we’re moving on to the second
<mrow>
nested in the<mfrac>
, which represents the denominator of the fraction. Compared to the numerator, the denominator is quite svelte, as it contains just the expression2a
.Again, another
⁢
to represent the implicit mathematical operation in2a
.And we’re done. Phew!
Figure 4-2 shows our quadratic formula rendered on a Web page in the Firefox browser for Mac.
MathML Comes Alive: An Interactive Quadratic Equation Solver
In the previous section, we constructed the quadratic formula in MathML. But we haven’t yet taken advantage of the rich capabilities that comes along with embedding MathML content in an HTML document. When you add HTML markup to a web page, you have the full power of CSS at your disposal to style your content and the scriptability of JavaScript to make the content dynamic. This is equally true of MathML, which means you can apply CSS and JavaScript to your equations in the same fashion you apply them to the rest of the HTML document.
Let’s use CSS and JavaScript to take our quadratic formula to the next level, and turn it into a quadratic equation solver, where the reader can input values for the constants in the quadratic formula (a, b, and c), and behind the scenes, a script will solve for x and output the solution(s). To start, we’ll first construct our solver in HTML and add CSS to style the constants in the equation for enhanced readability and comprehension. Then we’ll write JavaScript to add the interactive solving functionality.
Constructing the Equation Solver in HTML
For our interactive equation solver, let’s put together an HTML page with the following elements:
A brief explanation of quadratic equations, followed by a MathML representation of the standard quadratic equation (ax2+bx+c=0).
A MathML representation of the quadratic formula, which will be updated interactively with the values supplied by the reader.
A placeholder line following the quadratic formula (x = {?}), where the quadratic formula solutions will be displayed
Buttons the user can use to resize the quadratic equation and formula
An “equation solver” box, where the reader can input her own values for a, b, and c in order to obtain the solution.
Here is the HTML for piece #1, which includes a standard HTML
<head>
, an introductory paragraph about
quadratics, and our standard quadratic equation in MathML:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html>
<html
xmlns=
"http://www.w3.org/1999/xhtml"
>
<head>
<title>
Quadratic Equation Solver</title>
<!-- CSS <style> tag and JS <script> tags will be inserted here -->
</head>
<body>
<p>
Quadratic equations can be written in the form:</p>
<math
xmlns=
"http://www.w3.org/1998/Math/MathML"
class=
"size_3 resizable"
id=
"quadratic_equation"
>
<mrow>
<mi
class=
"constant a"
>
a</mi>
<msup>
<mi>
x</mi>
<mn>
2</mn>
</msup>
<mo>
+</mo>
<mi
class=
"constant b"
>
b</mi>
<mi>
x</mi>
<mo>
+</mo>
<mi
class=
"constant c"
>
c</mi>
<mo>
=</mo>
<mn>
0</mn>
</mrow>
</math>
All the MathML elements in the quadratic equation above are
covered in A Fifteen-Minute Introduction to MathML. However, there are a
few key attribute additions to some of these elements. First, an
id
attribute with the value
quadratic_equation
has been added to the root
<math>
element. Just as on HTML elements, this
serves as a unique identifier that we can use to reference this equation
from within CSS or JavaScript. Similarly, different
class
attributes have been added to specific
<mi>
elements. All constants are identified
with a class of constant
, as well as an additional
value of a
, b
, or
c
to indicate the specific constant identifier. These
class
values provide more granular categorization of
these constants and operates, in order to facilitate styling and
scripting of these elements.
Note
Note also that we have removed the
⁢
operators to indicate implicit
multiplication operations. While it certainly is good in theory to
include them, in practice, they cause rendering problems in both
Google Chrome and the iBooks reader, and it’s simpler and more
expedient to remove them than to attempt to employ fancy CSS styling
and/or custom entity declarations to work around the problems. I’d
definitely recommend leaving out
⁢
if you’re developing for
iBooks.
Now, here’s piece #2: the quadratic formula:
<p>
Here'
s the formula for solving quadratic equations</p>
<div
class=
"formula"
>
<math
xmlns=
"http://www.w3.org/1998/Math/MathML"
class=
"size_3 resizable"
id=
"quadratic_formula"
>
<mrow>
<mi>
x</mi>
<mo>
=</mo>
<mfrac>
<mrow>
<mo>
-</mo>
<mi
class=
"constant b"
>
b</mi>
<mi>
±
</mi>
<msqrt>
<mrow>
<msup>
<mi
class=
"constant b"
>
b</mi>
<mn>
2</mn>
</msup>
<mo>
-</mo>
<mn>
4</mn>
<mo
class=
"dot_operator"
>
⋅
</mo>
<mi
class=
"constant a"
>
a</mi>
<mo
class=
"dot_operator"
>
⋅
</mo>
<mi
class=
"constant c"
>
c</mi>
</mrow>
</msqrt>
</mrow>
<mrow>
<mn>
2</mn>
<mo
class=
"dot_operator"
>
⋅
</mo>
<mi
class=
"constant a"
>
a</mi>
</mrow>
</mfrac>
</mrow>
</math>
</div>
The above MathML is largely identical to that shown in Example 4-1, with just a couple noteworthy changes. Again,
we’ve added an id
on the root
<math>
element, to identify it as the quadratic
formula. We’ve also added class
values on the
<mi>
s representing the constants for
a, b, and
c; these class
es are consistent
with those used in the MathML for the quadratic equation. The other main
modification is that we are using explicit dot
operator characters (⋅
) to
indicate multiplication. (The reason for this is that we’ll be scripting
the quadratic formula to make the constants dynamically updateable, and
we’ll need a visible indicator to separate values when, say,
4ac gets changed to 4 ⋅ 3 ⋅
2).
Moving onto the third chunk of HTML, we’ll add our placeholder for the solution:
<div>
<math
xmlns=
"http://www.w3.org/1998/Math/MathML"
class=
"size_3 resizable"
id=
"formula_solution"
>
<mrow>
<mi>
x</mi><mo>
=</mo>
<!-- Wanted to use "mfenced" here, but it rendered badly in some browsers -->
<mtext>
{</mtext><mn
id=
"equation_solution"
>
?</mn><mo>
}</mo>
</mrow>
</math>
</div>
This will render in the browser as x = { ? }
.
As indicated in the comment before the <mo>
line, the ideal markup to use to specify braces around the solution
value would be the <mfenced>
element, which is specifically designed to block off
mathematical expressions with braces, parentheses, or brackets; however,
unfortunately, rendering support for <mfenced>
is not currently robust enough in Mobile Safari or the iBooks reader to
meet our needs here (the curly braces don’t render properly), so using
<mtext>
afforded wider compatibility. Note the
id
value of equation_solution
on
the <mn>
element currently holding the
?
; this is a placeholder where our equation solver
will drop in the solution to the quadratic formula after the user adds
her values for a, b, and
c.
We’ve now completed all the MathML markup. Now all that’s left are the interface elements for the user to interact with the equation solver. Here, in piece #4, we add two buttons that the user can use to resize the MathML equations:
<div
class=
"buttons"
>
<p>
<span
class=
"button"
id=
"minus_button"
>
−
</span>
<span
class=
"button"
id=
"plus_button"
>
+
</span>
<
-- Click these buttons to resize the quadratic equation and formula</p>
</div>
The buttons are inplemented as <span>
elements with a class
of button
,
and are wrapped in a <div>
with a
class
of buttons
; we’ll use these
class
attributes in the next section to style the
buttons.
Finally, for piece #5, we add a <form>
with fields where the user can input the constant values of her
choice:
<form
id=
"formula_values"
>
<h3>
Quadratic Equation Solver</h3>
<p>
Enter integer values for<span
class=
"a"
>
a</span>
,<span
class=
"b"
>
b</span>
, and<span
class=
"c"
>
c</span>
to solve a quadratic equation:</p>
<p><label
for=
"input_a"
class=
"a"
><em>
a:</em></label>
<input
id=
"input_a"
size=
"3"
/>
<span
id=
"up_a"
class=
"up_button"
>
▲
</span>
<span
id=
"down_a"
class=
"down_button"
>
▼
</span>
<label
for=
"input_b"
class=
"b"
><em>
b:</em></label>
<input
id=
"input_b"
size=
"3"
/>
<span
id=
"up_b"
class=
"up_button"
>
▲
</span>
<span
id=
"down_b"
class=
"down_button"
>
▼
</span>
<label
for=
"input_c"
class=
"c"
><em>
c:</em></label>
<input
id=
"input_c"
size=
"3"
/>
<span
id=
"up_c"
class=
"up_button"
>
▲
</span>
<span
id=
"down_c"
class=
"down_button"
>
▼
</span></p>
<p><input
id=
"solve_button"
type=
"submit"
value=
"Solve equation"
/></p>
<p
id=
"error_log"
>
Enter integer for<em>
a</em><br/>
Enter integer for<em>
b</em><br/>
Enter integer for<em>
c</em><br/>
</p>
</form>
The HTML above contains three <input>
fields where the user can enter her a,
b, and c values. Each
<input>
has an id
and a
corresponding <label>
that indicates which
constant value it takes. Next to each input, there are
<span>
s with class
values of
up_button
and down_button
, which
will be styled with CSS to create up-arrow and down-arrow buttons next
to each field, which the user can use to increment or decrement constant
values instead of typing in the fields directly.
Note
The up-arrow and down-arrow buttons will actually be a UI necessity for functionality in the iBooks reader, which does not provide access to the iOS keyboard for inputting data into form fields when you are reading standard EPUB books. (Keyboard access is, however, available when accessing widgets within iBooks Author–generated content.)
Styling the Equation Solver with CSS
Now that the HTML and MathML is in place, let’s style all this
content with CSS. You can apply styling to MathML using selectors and
properties in exactly the same fashion as is done for HTML. For example,
the following CSS will render all <mn>
elements
in your document in the color green, with a dashed border surrounding
them:
mn
{
color
:
green
;
border
:
dashed
;
}
There are three main styling tasks we’ll be accomplishing with our CSS:
Styling to support equation resizing via the buttons we created in HTML piece #4
Styling for the mathematical elements in the quadratic equation and quadratic formula
Styling for the user-interface elements in the “equation solver” box
To start off, here is the CSS to support setting the MathML equations at different sizes:
math
.size_1
{
font-size
:
1em
}
math
.size_2
{
font-size
:
2em
}
math
.size_3
{
font-size
:
3em
}
math
.size_4
{
font-size
:
4em
}
math
.size_5
{
font-size
:
5em
}
Each of these CSS selectors is in the format
math.size_
N
, where
N
is a number from 1 to 5. These selectors
will apply a font size of 1em
to
<math>
elements with a class
of size_1
, a font size of 2em
to
<math>
elements with a class
of size_2
, and so on. With this styling in place, all
we’ll need to do with JavaScript is change the class
on the <math>
elements to a different
size_
N
value, and the
equation size will automatically adjust accordingly.
Next, we have a set of CSS to style the math elements within each equation. We’ll customize the rendering of the constants, and the dot operator:
math
.constant
{
font-weight
:
bold
}
.a
{
color
:
red
}
.b
{
color
:
green
}
.c
{
color
:
blue
}
.dot_operator
{
color
:
gray
}
The first four lines above contain the styling for the constants.
All a, b, and
c constants have a class value containing
constant
, so they will all receive a bold font
weight. In addition to the bold styling, instances of each constant
letter are rendered in a different color: red for
a, green for b, and blue for
c.
For the quadratic formula, where we use the dot operator, we set a color of gray to soften the rendering a bit and make the dots less obtrusive.
Finally, we need to add some styling for the elements in the “equation solver” box:
#input_a
:focus
{
background-color
:
#F2B1B7
}
#input_b
:focus
{
background-color
:
#96CF91
}
#input_c
:focus
{
background-color
:
#B59EF7
}
span
.button
{
border
-
radius
:
3px
;
border
:
1px
solid
black
;
padding
:
1px
1px
1px
1px
;
font-size
:
2em
;
background-color
:
#C0C0C0
;
cursor
:
pointer
;
}
span
.up_button
{
position
:
relative
;
font-size
:
.5em
;
top
:
-.5em
;
cursor
:
pointer
;
}
span
.down_button
{
position
:
relative
;
font-size
:
.5em
;
top
:
.5em
;
left
:
-1.05em
;
cursor
:
pointer
;
}
form
#formula_values
{
padding
:
10px
;
border
:
1px
solid
black
;
width
:
400px
;
}
#error_log
{
color
:
red
;
}
The first three lines set a “focus” background color for the input fields in the equation solver box, which will be displayed when the field is active for text entry.
The span.button
block sets the styles for the
two “resize” buttons: a 1-pixel black border with a 3-pixel border
radius (for rounded corners), 1 pixel of padding between the button edge
and the button text, a font size of 2em
for the
button text, a background color of gray, and a “pointer” image for the
cursor when it hovers over the buttons.
The span.up_button
and
span.down_button
blocks serve to position the up and
down arrows for incrementing/decrementing the constant values in the
input fields; their locations are set relative to their existing
position in the document flow, which is directly after the corresponding
<input>
element. A font size of
.5em
is set for the arrow characters, and again the
cursor is set to be a pointer.
Note
The HTML5 <input type="number">
element
actually provides the exact functionality we’re creating above,
creating a field that accepts numerical input with up and down arrow
buttons to the right for incrementing and decrementing. However, at
the time of writing, not every modern Web browser supports
<input type="number">
(e.g., no support in
Firefox at this time), so I chose the above method for broader
comptability.
Next, in form#formula_values
, we set the
<form>
element representing the “equation
solver” box to be 400 pixels wide, give it a 1-pixel black border, and
set 10 pixels of padding between the border and the content
within.
Finally, for the #error_log
section, where
we’ll insert any error messages resulting from user input, we set a
color of red to make the text stand out a bit more.
With all the HTML and CSS now in place, let’s see how our page renders. Figure 4-4 shows the equation solver page as displayed in Safari for Mac.
Scripting the Equation Solver with JavaScript
With both HTML and CSS in place, all that’s left to do is make the solver interactive, which entails three steps:
Activate all the UI buttons and input fields, so that we can capture the user’s values for a, b, and c.
Update the corresponding constants in the MathML equations up top when the user supplies new values for a, b, and c.
Actually solve the quadratic equation for the constants supplied by the user, and print the solution in the
x = {?}
slot below the equations
We’ll wrap all our code in a MathMLApp()
function, which will be triggered when the window has loaded:
window
.
addEventListener
(
'load'
,
eventWindowLoaded
,
false
);
function
eventWindowLoaded
()
{
MathMLApp
();
}
function
MathMLApp
(){
// Our code for steps 1–3 will go here
}
For step 1, we will add event handlers and corresponding functions
that will be triggered to run when buttons or clicked, or the input
fields are deselected. We’ll use the jQuery bind()
function, which greatly simplifies the code required to
accomplish this task. To bind a function to a specific DOM event, the
code is as follows:
$
(
"SELECTOR"
).
bind
(
'EVENT'
,
FUNCTIONTORUN
);
Where SELECTOR
specifies a CSS selector
that indicates the page elements to which the function will be bound,
EVENT
specifies the user action on the
elements that will trigger the function, and
FUNCTIONTORUN
specifies the function to run
when the trigger occurs.
Note
You can also embed the function code directly within
bind()
, in which case the syntax looks like
this:
$
(
"SELECTOR"
).
bind
(
'EVENT'
,
function
(
e
)
{
// Put your function code here
});
Here’s all the code for the event handling of the UI elements in the equation solver:
$
(
"#plus_button"
).
bind
(
'click'
,
function
(
e
)
{
// Get all elements of @class "resizable"
$
(
".resizable"
).
each
(
function
()
{
var
resizableClass
=
$
(
this
).
attr
(
"class"
);
// Strip out "resizable" from class to get the size value
var
sizeValue
=
resizableClass
.
replace
(
/ resizable/
,
''
);
// Extract size value
formulaSizeClassPrefix
=
sizeValue
.
split
(
'_'
)[
0
];
existingFormulaSize
=
Number
(
sizeValue
.
split
(
'_'
)[
1
]);
newFormulaSize
=
existingFormulaSize
+
1
;
if
(
newFormulaSize
>
5
)
{
alert
(
"Formulas already displayed at maximum size"
);
// Exit jQuery each() loop
return
false
;
}
else
{
newFormulaSizeClass
=
formulaSizeClassPrefix
+
'_'
+
String
(
newFormulaSize
);
// Reconstruct new @class attribute and update element
newClassAttribute
=
newFormulaSizeClass
+
" resizable"
;
$
(
this
).
attr
(
'class'
,
newClassAttribute
);
}
});
});
$
(
"#minus_button"
).
bind
(
'click'
,
function
(
e
)
{
// Get all elements of @class "resizable"
$
(
".resizable"
).
each
(
function
()
{
var
resizableClass
=
$
(
this
).
attr
(
"class"
);
// Strip out "resizable" from class to get the size value
var
sizeValue
=
resizableClass
.
replace
(
/ resizable/
,
''
);
// Extract size value
formulaSizeClassPrefix
=
sizeValue
.
split
(
'_'
)[
0
];
existingFormulaSize
=
Number
(
sizeValue
.
split
(
'_'
)[
1
]);
newFormulaSize
=
existingFormulaSize
-
1
;
if
(
newFormulaSize
<
1
)
{
alert
(
"Formulas already displayed at minimum size"
);
// Exit jQuery each() loop
return
false
;
}
else
{
newFormulaSizeClass
=
formulaSizeClassPrefix
+
'_'
+
String
(
newFormulaSize
);
// Reconstruct new @class attribute and update element
newClassAttribute
=
newFormulaSizeClass
+
" resizable"
;
$
(
this
).
attr
(
'class'
,
newClassAttribute
);
}
});
});
$
(
".up_button"
).
bind
(
'click'
,
function
(
e
)
{
var
upButtonPressed
=
e
.
target
;
var
upButtonId
=
upButtonPressed
.
getAttribute
(
"id"
);
var
correspondingInputId
=
upButtonId
.
replace
(
/up/
,
'input'
);
var
correspondingInputElement
=
document
.
getElementById
(
correspondingInputId
);
var
currentInputValue
=
$
(
correspondingInputElement
).
val
();
if
(
currentInputValue
!=
""
)
{
var
newInputValue
=
parseInt
(
currentInputValue
)
+
1
;
$
(
correspondingInputElement
).
val
(
newInputValue
);
updateFormula
();
}
else
{
$
(
correspondingInputElement
).
val
(
"1"
);
updateFormula
();
}
});
$
(
".down_button"
).
bind
(
'click'
,
function
(
e
)
{
var
downButtonPressed
=
e
.
target
;
var
downButtonId
=
downButtonPressed
.
getAttribute
(
"id"
);
var
correspondingInputId
=
downButtonId
.
replace
(
/down/
,
'input'
);
var
correspondingInputElement
=
document
.
getElementById
(
correspondingInputId
);
var
currentInputValue
=
$
(
correspondingInputElement
).
val
();
if
(
currentInputValue
!=
""
)
{
var
newInputValue
=
parseInt
(
currentInputValue
)
-
1
;
$
(
correspondingInputElement
).
val
(
newInputValue
);
updateFormula
();
}
else
{
$
(
correspondingInputElement
).
val
(
"-1"
);
updateFormula
();
}
});
$
(
"#input_a"
).
bind
(
'blur'
,
updateFormula
);
$
(
"#input_b"
).
bind
(
'blur'
,
updateFormula
);
$
(
"#input_c"
).
bind
(
'blur'
,
updateFormula
);
$
(
"#solve_button"
).
bind
(
'click'
,
function
(
e
)
{
// Prevent default form-submission action and try to solve the equation
e
.
preventDefault
();
updateFormula
(
e
);
});
In the first two blocks, we bind functions to the elements with
the id
s plus_button
and
minus_button
, which are used to resize the MathML
equations on the page. When these buttons are clicked[6], we cycle through each element with a
class
value containing resizable
(all the <math>
elements), and get the size
information contained in the class value (in the format
size_
N
, where
N
is a number from 1 to 5). For the plus
button we update the class
by incrementing the size
number by 1, and for the minus button, we update the
class
by decrementing it by 1.
In the next two blocks, we bind functions to the up and down
arrows next to each input field. When the user clicks these buttons, we
locate the input field with the id
(of format
input_
a
) that corresponds
to the button id
(of format
up_
a
or
down_
a
), where
a
is the constant a
,
b
, or c
. If the input field
contains a value, for the up buttons, we increment it to the next
highest integer, and for the down buttons, we decrement it to the next
lowest integer. Then we call the updateFormula()
function to update the corresponding constant values in the quadratic
equation and quadratic formula.
In the third block, we do event handling for the three
<input>
fields, with id
s
input_a
, input_b
, and
input_c
. In all the previous
bind()
calls, we bound functions to the
click
event, which meant that the function code was
triggered when the user performed a “click” action (either a click with
the mouse, or a tap on a touchscreen device). In the case of the
<input>
fields, we’re instead binding to the
blur
event, which is triggered when the user
“deselects” a form field (either by tabbing away from it on the
keyboard, or by clicking/tapping on another element). This allows us to
track when a user exits the form field, to check if she has entered new
data. We bind the updateFormula()
function to the
blur
event, so that the MathML equations will be
updated every time the user leaves a field.
In the last block, we also bind a click
event
the “Solve equation” button, which triggers the
updateFormula()
function. Note that we also include
the line e.preventDefault()
, which suppresses the
default form-submission handling for our <form>
in favor of our custom JavaScript handling.
Now that we’ve got our event handling in place for all the buttons
and fields in the equation solver, we need to add the
updateFormula()
function, which will handle the task
of updating the values in the quadratic equation and quadratic formula
with the user’s input, as well as call a function for solving the
quadratic equation. Here’s the annotated
updateFormula()
code:
function
updateFormula
(
e
)
{
// Start out with no error text
var
errorText
=
""
;
var
aValue
=
$
(
"#input_a"
).
val
();
var
bValue
=
$
(
"#input_b"
).
val
();
var
cValue
=
$
(
"#input_c"
).
val
();
// Regex pattern for acceptable a values, which must be integers > 0
var
aPattern
=
/^-?[1-9][0-9]*$/
;
// Regex pattern for acceptable b/c values, which must be integers >= 0
var
bcPattern
=
/^(0|(-?[1-9][0-9]*))$/
;
if
(
aValue
==
""
)
{
errorText
+=
"Enter integer for <em>a</em><br/>"
;
// Reset aValue to default of "a"
aValue
=
"a"
;
// Reset answer to "?"
$
(
"#equation_solution"
).
text
(
"?"
);
}
else
if
(
!
(
aValue
.
match
(
aPattern
)))
{
errorText
+=
"Invalid value for <em>a</em>: "
+
aValue
+
"<br/>"
;
// Reset aValue to default of "a"
aValue
=
"a"
;
// Reset answer to "?"
$
(
"#equation_solution"
).
text
(
"?"
);
}
if
(
bValue
==
""
)
{
errorText
+=
"Enter integer for <em>b</em><br/>"
;
// Reset bValue to default of "b"
bValue
=
"b"
;
// Reset answer to "?"
$
(
"#equation_solution"
).
text
(
"?"
);
}
else
if
(
!
(
bValue
.
match
(
bcPattern
)))
{
errorText
+=
"Invalid value for <em>b</em>: "
+
bValue
+
"<br/>"
;
// Reset bValue to default of "b"
bValue
=
"b"
;
// Reset answer to "?"
$
(
"#equation_solution"
).
text
(
"?"
);
}
if
(
cValue
==
""
)
{
errorText
+=
"Enter integer for <em>c</em><br/>"
;
// Reset cValue to default of "c"
cValue
=
"c"
;
// Reset answer to "?"
$
(
"#equation_solution"
).
text
(
"?"
);
}
else
if
(
!
(
cValue
.
match
(
bcPattern
)))
{
errorText
+=
"Invalid value for <em>c</em>: "
+
cValue
+
"<br/>"
;
// Reset cValue to default of "c"
cValue
=
"c"
;
// Reset answer to "?"
$
(
"#equation_solution"
).
text
(
"?"
);
}
$
(
"math .a"
).
each
(
function
()
{
$
(
this
).
text
(
aValue
);
});
$
(
"math .b"
).
each
(
function
()
{
$
(
this
).
text
(
bValue
);
});
$
(
"math .c"
).
each
(
function
()
{
$
(
this
).
text
(
cValue
);
});
$
(
"#error_log"
).
html
(
errorText
);
// If error text is now empty, it means valid numbers have been entered
// for a, b, and c.
// Go ahead and solve the quadratic equation
if
(
errorText
==
""
)
{
solve_quadratic_equation
(
aValue
,
bValue
,
cValue
);
}
}
In this line, and the two following, we store the values currently in the input fields for a, b, and c in the variables
aValue
,bValue
, andcValue
, respectively.We need to do data validation on the data entered in the field for the a constant, which must be a nonzero integer (if a were equal to zero, the equation would cease to be quadratic, because the x2 term would disappear). Here, we define a regular expression that specifies this condition: our input can be only be a series of one or more digits that doesn’t start with 0, which may be preceded by an optional minus sign.
Here, we define a regular expression for the data validation requirements for the b and c constants. The rules are exactly the same as for a, except a value of 0 is also acceptable.
In this
if
block, we do our data validation for the a value. If the field is empty, we store a message in theerrorText
variable that prompts the user to enter an integer for a. If the field contains data, we check it against our regular expression for a, and if it fails validation, we store an “Invalid value” message in theerrorText
variable, and update ouraValue
with the default value ofa
and reset the equation solution to?
.This
if
block does the same data validation for the b value as for a.Again, same data validation for c as for a and b.
Now that we’ve completed data validation, in the next three blocks, we cycle through all the elements in our MathML equations that correspond to a, b, and c (the jQuery code
$("math .a").each()
runs a loop on all<math>
elements with a class value that containsa
) and set their text equal to the values stored inaValue
,bValue
, andcValue
.Remember, in the previous data-validation steps, if the user-supplied values for a, b, or c failed validation, we reverted
aValue
,bValue
, orcValue
back to their default (a
,b
, orc
, respectively), which means that if the user entered a bad value for a constant, the effect is that the value for that constant will be defaulted back to the constant name in the MathML equations.Here, we update our error log element with all the messages stored in the
errorText
variable, so that they will be displayed to the user.In this final
if
block, we check to see if theerrorText
variable is empty. If so, that means the user has supplied values for all three constants, and all values are valid, so we can go ahead and solve the equation. We call thesolve_quadratic_equation()
function, passing it the values of all three constants.
The last piece of code needed is the
solve_quadratic_equation()
function, which
follows:
function
solve_quadratic_equation
(
aValue
,
bValue
,
cValue
)
{
var
discriminant
=
(
bValue
*
bValue
)
-
4
*
aValue
*
cValue
;
var
equationSolution
=
""
;
if
(
discriminant
<
0
)
{
// No solution to equation; display null set
$
(
"#equation_solution"
).
text
(
"No Solution"
);
}
else
if
(
discriminant
==
0
)
{
equationSolution
=
-
bValue
/
(
2
*
aValue
);
$
(
"#equation_solution"
).
text
(
equationSolution
);
}
else
{
var
solution1
=
(
-
bValue
-
Math
.
sqrt
(
discriminant
))
/
(
2
*
aValue
);
var
solution2
=
(
-
bValue
+
Math
.
sqrt
(
discriminant
))
/
(
2
*
aValue
);
equationSolution
=
String
(
solution1
)
+
", "
+
String
(
solution2
);
$
(
"#equation_solution"
).
text
(
equationSolution
);
}
}
Our first step in solving the quadratic equation is to calculate the discriminant of our quadratic equation (equal to b2 − 4ac), which tells us how many solutions the equation has. If the discriminant is less than 0, the equation has no solution; if it’s equal to 0, there is one solution; and if it’s greater than 0, there are two solutions. In the case where there are no solutions, we replace the ? on the solution line below the quadratic formula with the text “No Solution”. If there is one solution, we print that value in the solution line. If there are two solutions, we print them both, separated by a comma, on the solution line.
Figure 4-5 shows the results of a successful equation-solve in the iBooks reader. Note that the quadratic equation and formula have had their a, b, and c values updated with the input from the fields below, and that the correct solution is displayed below the quadratic formula. Very cool!
Example 4-2 contains the full, final HTML and CSS for the equation solver, and Example 4-3 contains the full JavaScript. You can also download the code from the GitHub repository for HTML5 for Publishers. And don’t forget to try out the example for yourself in your ereader; it’s at the end of this chapter.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html>
<html
xmlns=
"http://www.w3.org/1999/xhtml"
>
<head>
<title>
Quadratic Equation Solver</title>
<script
src=
"mathml.js"
></script>
<script
src=
"jquery-1.6.2.min.js"
></script>
<style
media=
"screen"
type=
"text/css"
>
math
.size_1
{
font-size
:
1em
}
math
.size_2
{
font-size
:
2em
}
math
.size_3
{
font-size
:
3em
}
math
.size_4
{
font-size
:
4em
}
math
.size_5
{
font-size
:
5em
}
math
.constant
{
font-weight
:
bold
}
.a
{
color
:
red
}
.b
{
color
:
green
}
.c
{
color
:
blue
}
.dot_operator
{
color
:
gray
}
#input_a
:focus
{
background-color
:
#F2B1B7
}
#input_b
:focus
{
background-color
:
#96CF91
}
#input_c
:focus
{
background-color
:
#B59EF7
}
span
.button
{
border
-
radius
:
3px
;
border
:
1px
solid
black
;
padding
:
1px
1px
1px
1px
;
font-size
:
2em
;
background-color
:
#C0C0C0
;
cursor
:
pointer
;
}
span
.up_button
{
position
:
relative
;
font-size
:
.5em
;
top
:
-.5em
;
cursor
:
pointer
;
}
span
.down_button
{
position
:
relative
;
font-size
:
.5em
;
top
:
.5em
;
left
:
-1.05em
;
cursor
:
pointer
;
}
form
#formula_values
{
padding
:
10px
;
border
:
1px
solid
black
;
width
:
400px
;
}
#error_log
{
color
:
red
;
}
</style>
</head>
<body>
<p>
Quadratic equations can be written in the form:</p>
<math
xmlns=
"http://www.w3.org/1998/Math/MathML"
class=
"size_3 resizable"
id=
"quadratic_equation"
>
<mrow>
<mi
class=
"constant a"
>
a</mi>
<msup>
<mi>
x</mi>
<mn>
2</mn>
</msup>
<mo>
+</mo>
<mi
class=
"constant b"
>
b</mi>
<mi>
x</mi>
<mo>
+</mo>
<mi
class=
"constant c"
>
c</mi>
<mo>
=</mo>
<mn>
0</mn>
</mrow>
</math>
<p>
Here'
s the formula for solving quadratic equations</p>
<div
class=
"formula"
>
<math
xmlns=
"http://www.w3.org/1998/Math/MathML"
class=
"size_3 resizable"
id=
"quadratic_formula"
>
<mrow>
<mi>
x</mi>
<mo>
=</mo>
<mfrac>
<mrow>
<mo>
-</mo>
<mi
class=
"constant b"
>
b</mi>
<mi>
±
</mi>
<msqrt>
<mrow>
<msup>
<mi
class=
"constant b"
>
b</mi>
<mn>
2</mn>
</msup>
<mo>
-</mo>
<mn>
4</mn>
<mo
class=
"dot_operator"
>
⋅
</mo>
<mi
class=
"constant a"
>
a</mi>
<mo
class=
"dot_operator"
>
⋅
</mo>
<mi
class=
"constant c"
>
c</mi>
</mrow>
</msqrt>
</mrow>
<mrow>
<mn>
2</mn>
<mo
class=
"dot_operator"
>
⋅
</mo>
<mi
class=
"constant a"
>
a</mi>
</mrow>
</mfrac>
</mrow>
</math>
</div>
<div>
<math
xmlns=
"http://www.w3.org/1998/Math/MathML"
class=
"size_3 resizable"
id=
"formula_solution"
>
<mrow>
<mi>
x</mi><mo>
=</mo>
<!-- Wanted to use "mfenced" here, but it rendered badly in some browsers -->
<mtext>
{</mtext><mn
id=
"equation_solution"
>
?</mn><mo>
}</mo>
</mrow>
</math>
</div>
<div
class=
"buttons"
>
<p>
<span
class=
"button"
id=
"minus_button"
>
−
</span>
<span
class=
"button"
id=
"plus_button"
>
+
</span>
<
-- Click these buttons to resize the quadratic equation and formula</p>
</div>
<form
id=
"formula_values"
>
<h3>
Quadratic Equation Solver</h3>
<p>
Enter integer values for<span
class=
"a"
>
a</span>
,<span
class=
"b"
>
b</span>
, and<span
class=
"c"
>
c</span>
to solve a quadratic equation:</p>
<p><label
for=
"input_a"
class=
"a"
><em>
a:</em></label>
<input
id=
"input_a"
size=
"3"
/>
<span
id=
"up_a"
class=
"up_button"
>
▲
</span>
<span
id=
"down_a"
class=
"down_button"
>
▼
</span>
<label
for=
"input_b"
class=
"b"
><em>
b:</em></label>
<input
id=
"input_b"
size=
"3"
/>
<span
id=
"up_b"
class=
"up_button"
>
▲
</span>
<span
id=
"down_b"
class=
"down_button"
>
▼
</span>
<label
for=
"input_c"
class=
"c"
><em>
c:</em></label>
<input
id=
"input_c"
size=
"3"
/>
<span
id=
"up_c"
class=
"up_button"
>
▲
</span>
<span
id=
"down_c"
class=
"down_button"
>
▼
</span></p>
<p><input
id=
"solve_button"
type=
"submit"
value=
"Solve equation"
/></p>
<p
id=
"error_log"
>
Enter integer for<em>
a</em><br/>
Enter integer for<em>
b</em><br/>
Enter integer for<em>
c</em><br/>
</p>
</form>
</body>
</html>
window
.
addEventListener
(
'load'
,
eventWindowLoaded
,
false
);
function
eventWindowLoaded
()
{
MathMLApp
();
}
function
MathMLApp
(){
$
(
"#plus_button"
).
bind
(
'click'
,
function
(
e
)
{
// Get all elements of @class "resizable"
$
(
".resizable"
).
each
(
function
()
{
var
resizableClass
=
$
(
this
).
attr
(
"class"
);
// Strip out "resizable" from class to get the size value
var
sizeValue
=
resizableClass
.
replace
(
/ resizable/
,
''
);
// Extract size value
formulaSizeClassPrefix
=
sizeValue
.
split
(
'_'
)[
0
];
existingFormulaSize
=
Number
(
sizeValue
.
split
(
'_'
)[
1
]);
newFormulaSize
=
existingFormulaSize
+
1
;
if
(
newFormulaSize
>
5
)
{
alert
(
"Formulas already displayed at maximum size"
);
// Exit jQuery each() loop
return
false
;
}
else
{
newFormulaSizeClass
=
formulaSizeClassPrefix
+
'_'
+
String
(
newFormulaSize
);
// Reconstruct new @class attribute and update element
newClassAttribute
=
newFormulaSizeClass
+
" resizable"
;
$
(
this
).
attr
(
'class'
,
newClassAttribute
);
}
});
});
$
(
"#minus_button"
).
bind
(
'click'
,
function
(
e
)
{
// Get all elements of @class "resizable"
$
(
".resizable"
).
each
(
function
()
{
var
resizableClass
=
$
(
this
).
attr
(
"class"
);
// Strip out "resizable" from class to get the size value
var
sizeValue
=
resizableClass
.
replace
(
/ resizable/
,
''
);
// Extract size value
formulaSizeClassPrefix
=
sizeValue
.
split
(
'_'
)[
0
];
existingFormulaSize
=
Number
(
sizeValue
.
split
(
'_'
)[
1
]);
newFormulaSize
=
existingFormulaSize
-
1
;
if
(
newFormulaSize
<
1
)
{
alert
(
"Formulas already displayed at minimum size"
);
// Exit jQuery each() loop
return
false
;
}
else
{
newFormulaSizeClass
=
formulaSizeClassPrefix
+
'_'
+
String
(
newFormulaSize
);
// Reconstruct new @class attribute and update element
newClassAttribute
=
newFormulaSizeClass
+
" resizable"
;
$
(
this
).
attr
(
'class'
,
newClassAttribute
);
}
});
});
$
(
".up_button"
).
bind
(
'click'
,
function
(
e
)
{
var
upButtonPressed
=
e
.
target
;
var
upButtonId
=
upButtonPressed
.
getAttribute
(
"id"
);
var
correspondingInputId
=
upButtonId
.
replace
(
/up/
,
'input'
);
var
correspondingInputElement
=
document
.
getElementById
(
correspondingInputId
);
var
currentInputValue
=
$
(
correspondingInputElement
).
val
();
if
(
currentInputValue
!=
""
)
{
var
newInputValue
=
parseInt
(
currentInputValue
)
+
1
;
$
(
correspondingInputElement
).
val
(
newInputValue
);
updateFormula
();
}
else
{
$
(
correspondingInputElement
).
val
(
"1"
);
updateFormula
();
}
});
$
(
".down_button"
).
bind
(
'click'
,
function
(
e
)
{
var
downButtonPressed
=
e
.
target
;
var
downButtonId
=
downButtonPressed
.
getAttribute
(
"id"
);
var
correspondingInputId
=
downButtonId
.
replace
(
/down/
,
'input'
);
var
correspondingInputElement
=
document
.
getElementById
(
correspondingInputId
);
var
currentInputValue
=
$
(
correspondingInputElement
).
val
();
if
(
currentInputValue
!=
""
)
{
var
newInputValue
=
parseInt
(
currentInputValue
)
-
1
;
$
(
correspondingInputElement
).
val
(
newInputValue
);
updateFormula
();
}
else
{
$
(
correspondingInputElement
).
val
(
"-1"
);
updateFormula
();
}
});
$
(
"#input_a"
).
bind
(
'blur'
,
updateFormula
);
$
(
"#input_b"
).
bind
(
'blur'
,
updateFormula
);
$
(
"#input_c"
).
bind
(
'blur'
,
updateFormula
);
$
(
"#solve_button"
).
bind
(
'click'
,
function
(
e
)
{
// Prevent default form-submission action and try to solve the equation
e
.
preventDefault
();
updateFormula
(
e
);
});
function
updateFormula
(
e
)
{
// Start out with no error text
var
errorText
=
""
;
var
aValue
=
$
(
"#input_a"
).
val
();
var
bValue
=
$
(
"#input_b"
).
val
();
var
cValue
=
$
(
"#input_c"
).
val
();
// Regex pattern for acceptable a values, which must be integers > 0
var
aPattern
=
/^-?[1-9][0-9]*$/
;
// Regex pattern for acceptable b/c values, which must be integers >= 0
var
bcPattern
=
/^(0|(-?[1-9][0-9]*))$/
;
if
(
aValue
==
""
)
{
errorText
+=
"Enter integer for <em>a</em><br/>"
;
// Reset aValue to default of "a"
aValue
=
"a"
;
// Reset answer to "?"
$
(
"#equation_solution"
).
text
(
"?"
);
}
else
if
(
!
(
aValue
.
match
(
aPattern
)))
{
errorText
+=
"Invalid value for <em>a</em>: "
+
aValue
+
"<br/>"
;
// Reset aValue to default of "a"
aValue
=
"a"
;
// Reset answer to "?"
$
(
"#equation_solution"
).
text
(
"?"
);
}
if
(
bValue
==
""
)
{
errorText
+=
"Enter integer for <em>b</em><br/>"
;
// Reset bValue to default of "b"
bValue
=
"b"
;
// Reset answer to "?"
$
(
"#equation_solution"
).
text
(
"?"
);
}
else
if
(
!
(
bValue
.
match
(
bcPattern
)))
{
errorText
+=
"Invalid value for <em>b</em>: "
+
bValue
+
"<br/>"
;
// Reset bValue to default of "b"
bValue
=
"b"
;
// Reset answer to "?"
$
(
"#equation_solution"
).
text
(
"?"
);
}
if
(
cValue
==
""
)
{
errorText
+=
"Enter integer for <em>c</em><br/>"
;
// Reset cValue to default of "c"
cValue
=
"c"
;
// Reset answer to "?"
$
(
"#equation_solution"
).
text
(
"?"
);
}
else
if
(
!
(
cValue
.
match
(
bcPattern
)))
{
errorText
+=
"Invalid value for <em>c</em>: "
+
cValue
+
"<br/>"
;
// Reset cValue to default of "c"
cValue
=
"c"
;
// Reset answer to "?"
$
(
"#equation_solution"
).
text
(
"?"
);
}
$
(
"math .a"
).
each
(
function
()
{
$
(
this
).
text
(
aValue
);
});
$
(
"math .b"
).
each
(
function
()
{
$
(
this
).
text
(
bValue
);
});
$
(
"math .c"
).
each
(
function
()
{
$
(
this
).
text
(
cValue
);
});
$
(
"#error_log"
).
html
(
errorText
);
// If error text is now empty, it means valid numbers have been entered
// for a, b, and c.
// Go ahead and solve the quadratic equation
if
(
errorText
==
""
)
{
solve_quadratic_equation
(
aValue
,
bValue
,
cValue
);
}
}
function
solve_quadratic_equation
(
aValue
,
bValue
,
cValue
)
{
var
discriminant
=
(
bValue
*
bValue
)
-
4
*
aValue
*
cValue
;
var
equationSolution
=
""
;
if
(
discriminant
<
0
)
{
// No solution to equation; display null set
$
(
"#equation_solution"
).
text
(
"No Solution"
);
}
else
if
(
discriminant
==
0
)
{
equationSolution
=
-
bValue
/
(
2
*
aValue
);
$
(
"#equation_solution"
).
text
(
equationSolution
);
}
else
{
var
solution1
=
(
-
bValue
-
Math
.
sqrt
(
discriminant
))
/
(
2
*
aValue
);
var
solution2
=
(
-
bValue
+
Math
.
sqrt
(
discriminant
))
/
(
2
*
aValue
);
equationSolution
=
String
(
solution1
)
+
", "
+
String
(
solution2
);
$
(
"#equation_solution"
).
text
(
equationSolution
);
}
}
}
MathML Browser and Ereader Compatibility
There are varying levels of MathML support in modern Web browsers. The Gecko engine (used by Firefox) fully supports presentation MathML. The WebKit engine (used by Safari and Google Chrome) has partial rendering support for a subset of presentation MathML elements (including all the elements used in the quadratic formula), and as part of the WebKit Open Source Project, efforts are underway to implement MathML more fully. Internet Explorer does not have native rendering support for MathML, but in IE7 and later, you can install the free MathPlayer plugin from Design Science to add this functionality.
So, given the current state of the browser ecosystem’s MathML support, hoiw can HTML5 developers embed MathML content with confidence that a majority of visitors will be viewing their pages in MathML-compatible browsers, or in browsers with MathML plugins installed? If the onus is on the client side to be using the right software, there are no guarantees. Luckily, there’s a compatibility solution developers can implement: the MathJax project.
MathJax is an open source, JavaScript and CSS–based software package that is intended to provide full presentation MathML rendering support for browsers that have none, and to augment the MathML capabilities of browsers that have partial support—ensuring consistent, high-quality MathML rendering across all browsers with modern JavaScript and CSS support. See the MathJax Browser Compatibility page for a full list of supported browsers.
Note
In addition to MathML, MathJax also has rendering support for LATEX, a non-XML-based markup language for document production that has strong markup support for mathematical content. Many STM authors prefer formatting mathematical content in LATEX because it is possible to write and mark up equations manually, without the burden of heavy-duty XML tagging.
MathJAX doesn’t require users to install any plugins or other
software for their browsers; instead, as described in the MathJax docs,
developers just add the following <script>
tag to
their HTML document’s <head>
:
<script
type=
"text/javascript"
src=
"https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=
TeX-AMS-MML_HTMLorMML"></script>
which will load the MathJax engine from MathJax’s CDN. It’s also possible to download the MathJax package and host it locally on your own Web server; see the docs for more instructions on doing so.
So, what about MathML rendering support in the major ereaders? As of January 2013, per the latest data compiled by the Book Industry Study Group (BISG), both iBooks and the NOOK Color/Tablet (but not NOOK eInk devices) have MathML support. The iBooks engine is based on WebKit, so its MathML support is also partial, but good enough to render the quadratic formula (as shown in Figure 4-5).
It would be great if developers could embed MathJax in EPUB to add support for MathML in ereaders that cannot render MathML natively. However, not all ereaders have the JavaScript support necessary to run MathJax. On the MathJax site’s “EPUB readers page”, you can find a list of ereading systems that support MathML via MathJax.
If you’re interested in augmenting iBooks’s MathML support,[7] because iBooks has JavaScript support, you can indeed embed MathJax in your EPUB and get improved rendering. For best results, you’ll want to do some custom configuration specifically for iBooks. Peter Krautzberger has written up his recommended approach along with excellent step-by-step instructions in the post “How to include MathJax in an epub3 file to work with iBooks (and possibly others)”, which you should definitely read before attempting to embed MathJax in an EPUB.
Bibliography/Additional MathML resources
Some additional resources if you’re interested in learning more about MathML and/or doing your own MathML development:
- W3C “Mathematical Markup Language (MathML) Version 3.0” specification
The definitive source of information about the MathML specification, covering both presentation and content varieties of MathML. Contains clear and detailed descriptions of MathML element semantics and syntax, with markup samples and images illustrating expected rendering.
- W3C “Sample CSS Style Sheet for MathML (Non-Normative)”
This stylesheet provides some suggested default styling that MathML renders can use to ensure rendering in accordance with the MathML spec. It offers some good insights into how properties like
mathfamily
,mathcolor
, andmathsize
should be rendered, and also may be useful as a template if you are writing your own custom CSS for MathML content.- Firemath
Extension for Firefox that embeds a MathML editor in the browser. Good choice if you’re looking for a free, Web-based MathML creation tool.
- MathJax
Open source JavaScript and CSS–based engine that adds/augments MathML support in Web browsers (and also some ereaders). If you’re posting MathML content on the Web, most likely you will want to use MathJax. For more on MathJax and MathML compatibility, see MathML Browser and Ereader Compatibility.
- “How to include MathJax in an epub3 file to work with iBooks (and possibly others)” by Peter Krautzberger
Step-by-step guide to embedding MathJax in EPUB, primarily for use in the iBooks reader. Your mileage will vary in other ereaders (see MathML Browser and Ereader Compatibility)
[3] For more on “invisible operators”, see the MathML specification details at http://www.w3.org/TR/MathML2/chapter3.html#id.3.2.5.5.
[4] The quadratic formula is a fundamental equation covered in most first-year algebra curricula, used to solve single-variable equations that can be written in the form ax2+bx+c=0.
[5] For more on best practices for grouping expressions with <mrow>, see “Proper grouping of subexpressions using <mrow>” in Section 3.3.1 of the W3C MathML specification.
[6] The click
event corresponds to either a
click of the mouse, or a tap on a touchscreen device. For
conciseness, references to “click” in this section mean “click or
tap”.
[7] According to tests run by the W3C, the Safari 5.1 engine (based on WebKit, just like the iBooks engine) supports only 7 percent of presentation MathML’s capabilities; see http://www.w3.org/Math/testsuite/results/tests.html for more details.
Get HTML5 for Publishers now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.