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 expression 2πr, there are actually two implicit multiplication operators, as the equation could be written more explicitly as 2×π×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 &InvisibleTimes;. Here is a better representation of 2×π×r in MathML

<mn>2</mn> <mo>&InvisibleTimes;</mo> <mi>Π</mi> <mo>&InvisibleTimes;</mo>
<mi>r</mi>

For implicit function operations (e.g., log 10), use the entity &ApplyFunction;:

<mi>log</mi> <mo>&ApplyFunction;</mo> <mn>10</mn> <mo>=</mo> <mn>1</mn>  
<mrow>

Used to group subexpressions in a larger mathematical expression:

<mrow><mn>2</mn> <mo>&InvisibleTimes;</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>&InvisibleTimes;</mo><mi>x</mi></mrow>
  <mrow><mn>3</mn><mo>&InvisibleTimes;</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>
Editing an equation in FireMath in the Firefox Web Browser
Figure 4-1. Editing an equation in FireMath in the Firefox Web Browser

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:

Equation 4-1. The quadratic formula
The quadratic formula

Example 4-1 shows the quadratic formula constructed in presentation MathML, with annotations explaining each chunk of the markup:

Example 4-1. MathML markup for the quadratic formula
<math xmlns="http://www.w3.org/1998/Math/MathML"> 1
  <mrow> 2
    <mi>x</mi>
    <mo>=</mo>
    <mfrac> 3
      <mrow> 4
        <mo>-</mo>
        <mi>b</mi>
        <mi>&#xb1;</mi> 5
        <msqrt> 6
          <mrow> 7
            <msup> 8
              <mi>b</mi>
              <mn>2</mn>
            </msup>
            <mo>-</mo>
            <mn>4</mn>
            <mo>&InvisibleTimes;</mo> 9
            <mi>a</mi>
            <mo>&InvisibleTimes;</mo>
            <mi>c</mi>
          </mrow>
        </msqrt>
      </mrow>
      <mrow> 10
        <mn>2</mn>
        <mo>&InvisibleTimes;</mo> 11
        <mi>a</mi>
      </mrow>
    </mfrac>
  </mrow>
</math> 12
1

Every block of MathML needs to be nested in a root <math> element. Note also the namespace declaration xmlns="http://www.w3.org/1998/Math/MathML" to ensure browsers and ereaders recognize the content as MathML.

2

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 x= on the left side of the equation, tagging x as a variable and the equals sign as an operator

3

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.

4

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.

5

The content &#xb1; is a Unicode hex entity that represents the “plus-minus-sign” character.

6

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±.

7

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.

8

The <msup> element represents the b2 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.

9

Note the two occurrences of &InvisibleTimes; in the 4ac portion of the square root, which explicitly call out the two implicit multiplication operations being performed (4 times a times c).

10

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 expression 2a.

11

Again, another &InvisibleTimes; to represent the implicit mathematical operation in 2a.

12

And we’re done. Phew!

Figure 4-2 shows our quadratic formula rendered on a Web page in the Firefox browser for Mac.

Quadratic formula MathML embedded in a Web page on the Firefox browser
Figure 4-2. Quadratic formula MathML embedded in a Web page on the Firefox browser
Rendering of the same bitmap equation graphic on Kindle Paperwhite (left) and Kindle for iPad (right)
Figure 4-3. Rendering of the same bitmap equation graphic on Kindle Paperwhite (left) and Kindle for iPad (right)

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:

  1. A brief explanation of quadratic equations, followed by a MathML representation of the standard quadratic equation (ax2+bx+c=0).

  2. A MathML representation of the quadratic formula, which will be updated interactively with the values supplied by the reader.

  3. A placeholder line following the quadratic formula (x = {?}), where the quadratic formula solutions will be displayed

  4. Buttons the user can use to resize the quadratic equation and formula

  5. 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 &InvisibleTimes; 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 &InvisibleTimes; if you’re developing for iBooks.

Now, here’s piece #2: the quadratic formula:

<p>Here&apos;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>&#xb1;</mi>
        <msqrt>
          <mrow>
            <msup>
              <mi class="constant b">b</mi>
              <mn>2</mn>
            </msup>
            <mo>-</mo>
            <mn>4</mn>
            <mo class="dot_operator">&#x22c5;</mo>
            <mi class="constant a">a</mi>
            <mo class="dot_operator">&#x22c5;</mo>
            <mi class="constant c">c</mi>
          </mrow>
        </msqrt>
      </mrow>
      <mrow>
        <mn>2</mn>
        <mo class="dot_operator">&#x22c5;</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 classes 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 (&#x22c5;) 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">&#x2212;</span>
<span class="button" id="plus_button">&#x2b;</span>
&lt;-- 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">&#x25b2;</span>
<span id="down_a" class="down_button">&#x25bc;</span>
<label for="input_b" class="b"><em>b:</em></label>
<input id="input_b"  size="3"/> <span id="up_b" class="up_button">&#x25b2;</span>
<span id="down_b" class="down_button">&#x25bc;</span>
<label for="input_c" class="c"><em>c:</em></label>
<input id="input_c"  size="3"/> <span id="up_c" class="up_button">&#x25b2;</span>
<span id="down_c" class="down_button">&#x25bc;</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:

  1. Styling to support equation resizing via the buttons we created in HTML piece #4

  2. Styling for the mathematical elements in the quadratic equation and quadratic formula

  3. 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.

Equation solver in Safari for Mac
Figure 4-4. Equation solver 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:

  1. Activate all the UI buttons and input fields, so that we can capture the user’s values for a, b, and c.

  2. Update the corresponding constants in the MathML equations up top when the user supplies new values for a, b, and c.

  3. 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 ids 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 ids 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(); 1
    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]*$/; 2
    // Regex pattern for acceptable b/c values, which must be integers >= 0
    var bcPattern = /^(0|(-?[1-9][0-9]*))$/; 3
    if (aValue == "") { 4
        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 == "") { 5
        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 == "") { 6
        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() { 7
        $(this).text(aValue);
    });
    $("math .b").each(function() {
        $(this).text(bValue);
    });
    $("math .c").each(function() {
        $(this).text(cValue);
    });
    $("#error_log").html(errorText); 8
    // 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 == "") { 9
        solve_quadratic_equation(aValue, bValue, cValue);
    }
}
1

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, and cValue, respectively.

2

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.

3

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.

4

In this if block, we do our data validation for the a value. If the field is empty, we store a message in the errorText 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 the errorText variable, and update our aValue with the default value of a and reset the equation solution to ?.

5

This if block does the same data validation for the b value as for a.

6

Again, same data validation for c as for a and b.

7

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 contains a) and set their text equal to the values stored in aValue, bValue, and cValue.

Remember, in the previous data-validation steps, if the user-supplied values for a, b, or c failed validation, we reverted aValue, bValue, or cValue back to their default (a, b, or c, 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.

8

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.

9

In this final if block, we check to see if the errorText 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 the solve_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!

Success! We have solved 2x2+5x+2
Figure 4-5. Success! We have solved 2x2+5x+2

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.

Example 4-2. Final HTML and CSS for the MathML Equation Solver (mathml.html)
<?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&apos;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>&#xb1;</mi>
        <msqrt>
          <mrow>
            <msup>
              <mi class="constant b">b</mi>
              <mn>2</mn>
            </msup>
            <mo>-</mo>
            <mn>4</mn>
            <mo class="dot_operator">&#x22c5;</mo>
            <mi class="constant a">a</mi>
            <mo class="dot_operator">&#x22c5;</mo>
            <mi class="constant c">c</mi>
          </mrow>
        </msqrt>
      </mrow>
      <mrow>
        <mn>2</mn>
        <mo class="dot_operator">&#x22c5;</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">&#x2212;</span>
<span class="button" id="plus_button">&#x2b;</span>
&lt;-- 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">&#x25b2;</span>
<span id="down_a" class="down_button">&#x25bc;</span>
<label for="input_b" class="b"><em>b:</em></label>
<input id="input_b"  size="3"/> <span id="up_b" class="up_button">&#x25b2;</span>
<span id="down_b" class="down_button">&#x25bc;</span>
<label for="input_c" class="c"><em>c:</em></label>
<input id="input_c"  size="3"/> <span id="up_c" class="up_button">&#x25b2;</span>
<span id="down_c" class="down_button">&#x25bc;</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>
Example 4-3. Final JavaScript for the MathML Equation Solver (mathml.js)
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, and mathsize 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.