These are the ramblings of Matthijs Kooijman, concerning the software he hacks on, hobbies he has and occasionally his personal life.
Most content on this site is licensed under the WTFPL, version 2 (details).
Questions? Praise? Blame? Feel free to contact me.
My old blog (pre-2006) is also still available.
Sun | Mon | Tue | Wed | Thu | Fri | Sat |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
(...), Arduino, AVR, BaRef, Blosxom, Book, Busy, C++, Charity, Debian, Electronics, Examination, Firefox, Flash, FreeBSD, Gnome, Hardware, Inter-Actief, IRC, JTAG, LARP, Layout, Linux, Madness, Mail, Math, MS-1013, Mutt, Nerd, Notebook, Optimization, Personal, Plugins, QEMU, Random, Rant, S270, Sailing, Samba, Sanquin, Script, Sleep, Software, SSH, Study, Symbols, Tika, Travel, Trivia, Windows, Work, Xanthe, XBee
For this blog, I wanted to include some nicely-formatted formulas. An easy way to do so, is to use MathJax, a javascript-based math processor where you can write formulas using (among others) the often-used Tex math syntax.
However, I use Markdown to write my blogposts and including formulas directly in the text can be problematic because Markdown might interpret part of my math expressions as Markdown and transform them before MathJax has had a chance to look at them. In this post, I present a customized MathJax configuration that solves this problem in a reasonable elegant way.
An obvious solution is to put the match expression in Markdown code
blocks (or inline code using backticks), but by default MathJax does not
process these. MathJax can be reconfigured to also typeset the contents
of <code>
and/or <pre>
elements, but since actual code will likely
contain parts that look like math expressions, this will likely cause
your code to be messed up.
This problem was described in more detail by Yihui Xie in a
blogpost, along with a solution that preprocesses the DOM to look
for <code>
tags that start and end with an math expression start and
end marker, and if so strip away the <code>
tag so that MathJax will
process the expression later. Additionally, he translates any expression
contained in single dollar signs (which is the traditional Tex way to
specify inline math) to an expression wrapped in \(
and \)
, which is
the only way to specify inline math in MathJax (single dollars are
disabled since they would be too likely to cause false positives).
I considered using his solution, but it explicitly excludes code blocks
(which are rendered as a <pre>
tag containing a <code>
tag in
Markdown), and I wanted to use code blocks for centered math expressions
(since that looks better without the backticks in my Markdown source).
Also, I did not really like that the script modifies the DOM and has a
bunch of regexes that hardcode what a math formula looks like.
So I made an alternative implementation that configures MathJax to
behave as intended. This is done by overriding the normal automatic
typesetting in the pageReady
function and instead explicitly
typesetting all code tags that contain exactly one math expression.
Unlike the solution by Yihui Xie, this:
<code>
elements (e.g. no formulas in normal text), because the default
typesetting is replaced.<code>
elements inside <pre>
elements
(but this can be easily changed using the parent tag check from Yihui
Xie's code).pageReady
event, so the script does not have
to be at the end of the HTML page.You can find the MathJax configuration for this inline at the end of this post. To use it, just put the script tag in your HTML before the MathJax script tag (or see the MathJax docs for other ways).
To use it, just use the normal tex math syntax (using single or double $
signs) inside a code block (using backticks or an indented block) in any
combination. Typically, you would use single $
delimeters together with
backticks for inline math. You'll have to make sure that the code block
contains exactly a single MathJax expression (and maybe some whitespace), but
nothing else. E.g. this Markdown:
Formulas *can* be inline: `$z = x + y$`.
Renders as: Formulas can be inline: $z = x + y$
.
The double $$
delimeter produces a centered math expression. This works
within backticks (like Yihui shows) but I think it looks better in the Markdown
if you use an indented block (which Yihui's code does not support). So for
example this Markdown (note the indent):
$$a^2 + b^2 = c^2$$
Renders as:
$$a^2 + b^2 = c^2$$
Then you can also use more complex, multiline expressions. This indented block of Markdown:
$$
\begin{vmatrix}
a & b\\
c & d
\end{vmatrix}
=ad-bc
$$
Renders as:
$$
\begin{vmatrix}
a & b\\
c & d
\end{vmatrix}
=ad-bc
$$
So, here is the script that I am now using on this blog:
<script type="text/javascript">
MathJax = {
options: {
// Remove <code> tags from the blacklist. Even though we pass an
// explicit list of elements to process, this blacklist is still
// applied.
skipHtmlTags: { '[-]': ['code'] },
},
tex: {
// By default, only \( is enabled for inline math, to prevent false
// positives. Since we already only process code blocks that contain
// exactly one math expression and nothing else, it is also fine to
// use the nicer $...$ construct for inline math.
inlineMath: { '[+]': [['$', '$']] },
},
startup: {
// This is called on page ready and replaces the default MathJax
// "typeset entire document" code.
pageReady: function() {
var codes = document.getElementsByTagName('code');
var to_typeset = [];
for (var i = 0; i < codes.length; i++) {
var code = codes[i];
// Only allow code elements that just contain text, no subelements
if (code.childElementCount === 0) {
var text = code.textContent.trim();
inputs = MathJax.startup.getInputJax();
// For each of the configured input processors, see if the
// text contains a single math expression that encompasses the
// entire text. If so, typeset it.
for (var j = 0; j < inputs.length; j++) {
// Only use string input processors (e.g. tex, as opposed to
// node processors e.g. mml that are more tricky to use).
if (inputs[j].processStrings) {
matches = inputs[j].findMath([text]);
if (matches.length == 1 && matches[0].start.n == 0 && matches[0].end.n == text.length) {
// Trim off any trailing newline, which otherwise stays around, adding empty visual space below
code.textContent = text;
to_typeset.push(code);
code.classList.add("math");
if (code.parentNode.tagName == "PRE")
code.parentNode.classList.add("math");
break;
}
}
}
}
}
// Code blocks to replace are collected and then typeset in one go, asynchronously in the background
MathJax.typesetPromise(to_typeset);
},
},
};
</script>
Update 2020-08-05: Script updated to run typesetting only once, and
use typesetPromise
to run it asynchronously, as suggested by Raymond
Zhao in the comments below.
Update 2020-08-20: Added some Markdown examples (the same ones Yihui Xie used), as suggested by Troy.
Hey, this script works great! Just one thing: performance isn't the greatest. I noticed that upon every call to MathJax.typeset, MathJax renders the whole document. It's meant to be passed an array of all the elements, not called individually.
So what I did was I put all of the code elements into an array, and then called MathJax.typesetPromise (better than just typeset) on that array at the end. This runs much faster, especially with lots of LaTeX expressions on one page.
Hey Raymond, excellent suggestion. I've updated the script to make these changes, works perfect. Thanks!
What a great article! Congratulations :)
Can you please add a typical math snippet from one of your .md files? (Maybe the same as the one Yihui Xie uses in his post.)
I would like to see how you handle inline/display math in your markdown.
Hey Troy, good point, examples would really clarify the post. I've added some (the ones from Yihui Xie indeed) that show how to use this from Markdown. Hope this helps!