解决 MathJax 与 Markdown 的冲突

MathJax 是一个 JavaScript 引擎,能够将 LaTeX 语法书写的公式在网页上显示出来,而且效果杠杠的。Markdown 是一种轻量级的标记语言。用 Markdown 书写的文章,可以用 Markdown 解释器处理成标准的 HTML 文档。因此 Markdown 很适合用来写网络日志。

作为数学系毕业的学生,写博客时不可避免地会涉及到一些数学公式。可惜不巧,用 Markdown 写博客的我,在使用 MathJax 的时候遇到了一点麻烦。

在 Markdown 中,下划线 _ 被保留,用作标记符号。比如 _Slant_ 会生成倾斜的 Slant。在 LaTeX 中,下划线 _ 被用作下标记号。比如 x_i 会生成 $x_i$

由于 Markdown 在 MathJax 之前起作用,有时下标记号会被 Markdown 吃掉,变成 HTML 标记 <i> 而失去 LaTeX 的下标效果,造成数学公式显示不正常。比如 This is an example: $f_i = f_{i + 1}$ 里的两个下划线会被 Markdown 理解成倾斜的标记,显示成这样:This is an example: $fi = f{i + 1}$,这就不对了。

分析与思路

问题的本质在于,下划线 _ 首先被 Markdown 处理,变成 HTML 标记 <i>。之后,MathJax 会认为相关代码是不合法的公式代码,不予处理。比如上述例子,在 MathJax 看来会变成这样:This is an example: $f<i>i = f<i>{i + 1}$——这是什么鬼(MathJax 内心独白)。因此,我们要做的,就是在 Markdown 处理文本的时候,将公式中的下划线保留起来;然后,再让 MathJax 去处理保留下来的下划线。

在 Markdown 中,我们可以用 backtip (`) 来做原样抄写(Verbatim)。也就是说,包含在两个 ` 中间的内容,会被 Markdown 原样保留下来。这正符合我们「保护下划线」的需求。于是,上述例子应该写成 This is an example: `$f_i = f_{i + 1}$`

问题看似解决了,不过却又引发了下一个问题。被反引号包括的部分,被 Markdown 当做代码来处理,会加上 HTML 标签 <code>。这样一来,MathJax 在处理文档时,就会跳过这一部分——虽然下划线被保留了,但是整个公式都被忽略了。

一个妥协的办法,是用 JavaScript 处理所有的 <code> 标签:如果发现 <code> 标签内包含数学公式(以美元符号 \$ 开头结尾),那么就剥离标签,并调用 MathJax 处理。

下面我们来实现这一思路。

JavaScript 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
<script type="text/javascript">
$(document).ready(function(){
$("code").map(function(){
match = /^\$(.*)\$$/.exec($(this).html());
if (match){
//$(this).after("<span class=mathjax_inline>" + match + "</span>");
//$(this).hide();
$(this).replaceWith("<span class=hpl_mathjax_inline>" + $(this).html() + "</span>");
MathJax.Hub.Queue(["Typeset",MathJax.Hub,$(this).get(0)]);
}
});
});
</script>

这里,我们用正则表达式匹配了 <code> 标签内的美元符号,然后将 <code> 标签替换成 <span class=hpl_mathjax_inline> 标签,最后调用 MathJax.Hub.Queue 处理数学公式。

经过这样一番处理,This is an example: `$f_i = f_{i + 1}$` 就可以被正确处理为「This is an example: $f_i = f_{i + 1}$」了。

热评文章