MathJax 简介 与 实例

* 注:本文由一场 Presentation 的 nodePPT 整理而来。

1 什么是 MathJax

MathJax is a cross-browser JavaScript library that displays mathematical notation in web browsers, using MathML, TeX and AsciiMath markup.

MathJax 是一个跨浏览器的 JavaScript 库,可以在网页上显示数学式,使用 MathML, TeX 和 AsciiMath 标记语言编写。

这并没有很好地回答问题。你会接着问,什么是 MathML, TeX 和 AsciiMath ? 什么是 标记语言?

数学式工具小史

  • 1969,高德纳 (D. E. Knuth) 的巨著 《计算机程序设计艺术 (The Art of Computer Programming)》 第一卷出版
  • 1976,他收到 TACP 第二版,排版质量非常糟糕,他开始寻找高质量的数字排版系统
  • 1977,遍寻无果后,他开始自己动手写。 计划 1978 年完成,结果一直写到 1989 年,这就是 TeX 。 由于对 数学式 排版的良好支持,在学术写作中大受欢迎,并影响了日后的排版系统
  • 1985,计算机科学家 兰伯特 (L. Lamport) 基于 TeX 开发出宏包 LaTeX ,包含大多数需要的宏,方便一般人使用(注:由于 LaTeX 的流行,网上说 TeX 的时候,有时其实是在说 LaTeX )
  • 1999,W3C (万维网联盟) 提出 基于 XML 标准来描述 数学符号 和 公式的标准 MathML ,最新的版本是 2010年 发布的 3.0
  • 2009,美国数学学会 (American Mathematical Society) 为首的多个组织发起 MathJax,兼容 TeX, AsciiMath, MathML

( * 没找到 AsciiMath 的出处和年份。 最早可能出自 07 年的一篇论文,目的是为了简化 MathML 书写。简单说,AsciiMath 的设计目标是,只用 Ascii 字符就能写出数学公式。)

换言之,前面出现了几个优秀的 编写 / 排版 数学式 的 语言 / 工具 (LaTeX, MathML, AsciiMath), AMS 发起 MathJax 将它们集成到一起,用不同的语言写,最终都能转换成 MathML 在浏览器显示。

2 做个对比

这些名字长得很像,几乎都带一个 Math,通过对比来区分一下:

例如显示公式

$$x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$$

MathML

先看 MathML。

MathML 是 XML 的拓展,特点是:冗余精确

它可以同时表示 视觉义逻辑义,对读屏软件友好。

首先是显示效果:

x = b ± b 2 4 a c 2 a

然后源码长这样:

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
<math mode="display" xmlns="http://www.w3.org/1998/Math/MathML">
<mrow>
<mi>x</mi>
<mo>=</mo>
<mfrac>
<mrow>
<mo form="prefix">&#x2212;<!-- − --></mo>
<mi>b</mi>
<mo>&#x00B1;<!-- &PlusMinus; --></mo>
<msqrt>
<msup>
<mi>b</mi>
<mn>2</mn>
</msup>
<mo>&#x2212;<!-- − --></mo>
<mn>4</mn>
<mo>&#x2062;<!-- &InvisibleTimes; --></mo>
<mi>a</mi>
<mo>&#x2062;<!-- &InvisibleTimes; --></mo>
<mi>c</mi>
</msqrt>
</mrow>
<mrow>
<mn>2</mn>
<mo>&#x2062;<!-- &InvisibleTimes; --></mo>
<mi>a</mi>
</mrow>
</mfrac>
</mrow>
</math>

可以留意到,一些视觉上被省略不显示的符号 (典型的例子是 代数间省略的 乘号 InvisibleTimes),在 MathML 中也有对应的转义实体。这些符号的存在,首先保证公式 没有歧义 ;其次,读屏软件 可以识别这些符号并读出来,方便残障人士理解。

不过精确的代价,是相当地冗余。因为它本来就是设计给浏览器解析的。

LaTeX

显示效果:

$$x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$$

显示效果并不会有区别。浏览器不认识 LaTeX , LaTeX 写的公式到达浏览器,要经一层 js 库 转换成 MathML ,MathML 才是 W3C 制定的浏览器标准。

LaTeX 源码:

1
$$x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$$

源码比 MathML 精简很多。

打开浏览器的调试模式,可以看到浏览器解析的代码(js 转换后,浏览器解析前):

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
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
<mi>x</mi>
<mo>=</mo>
<mfrac>
<mrow>
<mo></mo>
<mi>b</mi>
<mo>±</mo>
<msqrt>
<msup>
<mi>b</mi>
<mn>2</mn>
</msup>
<mo></mo>
<mn>4</mn>
<mi>a</mi>
<mi>c</mi>
</msqrt>
</mrow>
<mrow>
<mn>2</mn>
<mi>a</mi>
</mrow>
</mfrac>
</math>

LaTeX 经转换后,显示效果一致; 转换得到的 MathML 代码差别也不大,但 不可见符号并不能自动理解并且添加

AsciiMath

显示效果不对:

$$x = (-b +- sqrt(b^2 – 4ac)) / (2a)$$

源码:

1
$$x = (-b +- sqrt(b^2 – 4ac)) / (2a)$$

生成的MathML:

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
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
<mi>x</mi>
<mo>=</mo>
<mo stretchy="false">(</mo>
<mo></mo>
<mi>b</mi>
<mo>+</mo>
<mo></mo>
<mi>s</mi>
<mi>q</mi>
<mi>r</mi>
<mi>t</mi>
<mo stretchy="false">(</mo>
<msup>
<mi>b</mi>
<mn>2</mn>
</msup>
<mo></mo>
<mn>4</mn>
<mi>a</mi>
<mi>c</mi>
<mo stretchy="false">)</mo>
<mo stretchy="false">)</mo>
<mrow class="MJX-TeXAtom-ORD">
<mo>/</mo>
</mrow>
<mo stretchy="false">(</mo>
<mn>2</mn>
<mi>a</mi>
<mo stretchy="false">)</mo>
</math>

写下这些文字时,我是在 nodePPT 里面加载 MathJax。再次进入调试模式,看看加载配置:

1
2
3
4
5
6
7
8
<!-- MathJax -->
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
config: ["TeX-AMS-MML_HTMLorMML.js"],
tex2jax: {inlineMath: [['\\(','\\)']]}});
//['$','$'], 避免货币价格冲突
</script>
<script type="text/javascript" src="/js/mathjax/MathJax.js"></script>

我给不熟悉的读者翻译一下这块代码:

配置并加载 MathJax.js,加载的配置文件(”TeX-AMS-MML_HTMLorMML.js”)意思是,加载 TeX + AMS拓展 + MathML,并使用HTML+MathML 显示。

没有加载 AsciiMath。


我尝试把配置改成这样

1
2
3
4
5
6
7
<!-- MathJax -->
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
config: ["TeX-MML-AM_HTMLorMML.js"],
tex2jax: {inlineMath: [['\\(','\\)']]}});
//['$','$'], 避免货币价格冲突
</script>

* 翻译:加载 TeX + MathML + AsciiMath

出于未知原因,没有配置文件同时兼容 AMS 拓展 和 AsciiMath (一般这意味着他们之间有冲突,例如同一个符号做了不同用途)

与此同时,查看配置文件后,发现

1
2
3
4
5
MathJax.Extension.asciimath2jax={
version:"2.6.0",
config:{
delimiters:[["`","`"]],
......

* 翻译:AsciiMath 用 反引号 作为起止符

把源码改成

1
\`x = (-b +- sqrt(b^2 – 4ac)) / (2a)\`

效果就出来了。(注:为兼容大多数公式,本文加载默认配置,所以 AsciiMath 无法正常显示。其实正常显示也会转换成 MathML ,效果是没有差别的。)

为什么源码多了一个反斜杠?

反引号 ``` 在 Markdown 代码块的起止符(没错这个反引号的代码块本身就是用反引号括起来的。好拗口。),所以需要增加反斜杠引用它本身,避免被识别为代码块(需要识别为数学块)。看来 AsciiMath 不仅跟别的工具冲突,跟 Markdown 也有冲突。

小结

  • LaTeX

    • 科学写作主流
    • 键盘有的符号直接输入
    • 没有的符号 或者 效果,通过 \关键字 转义, {} 表示起止范围

    如: 分数,就是 \frac 分子 分母 ,如果 分子 和 分母 只有一个符号,那就直接写,否则就要用花括号括起来

  • MathML

    • W3C 标准,精确,同时啰嗦
    • 在 MathJax 环境下无论用什么写公式,最终转换成 MathML 显示
    • 直接写 MathML,MathJax 会跳过转换,直接渲染
  • AsciiMath

    • nodePPT 的默认配置没有加载
    • 纯 Ascii 字符,不用转义符,阅读起来非常自然,理解不费劲; 但无法区分特殊字符和普通字符,会带来一些麻烦(例如:sqrt 究竟是 平方根,还是 4 个代数相乘呢?)
    • 跟其他工具有冲突

初步结论

  • 大多数情况下用 LaTeX
  • 极少数情况,需要用 MathML
  • 多种工具混合的环境,AsciiMath 比较容易出冲突,不推荐

3 怎么开始

加载和配置

HTML 里加载 MathJax,官方推荐做法是在 <head> 标签加入

1
2
3
<script type="text/javascript" async
src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML">
</script>

如果使用 nodePPT, 模板已经加载好

1
2
3
4
5
6
7
8
<!-- MathJax -->
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
config: ["TeX-AMS-MML_HTMLorMML.js"],
tex2jax: {inlineMath: [['\\(','\\)']]}});
//['$','$'], 避免货币价格冲突
</script>
<script type="text/javascript" src="/js/mathjax/MathJax.js"></script>

数学环境

使用 MathJax 时,需要特定标志,将 公式代码 的范围标识出来。在标志中间的部分,就叫做 数学环境 。包括 段模式 (displayed)行内 (inline) 两种。

查看配置或文档,可以发现 LaTeX 默认分隔符是:

1
2
3
4
5
......
config:{
inlineMath:[["\\(","\\)"]],
displayMath:[["$$","$$"],["\\[","\\]"]],
......

配置可以在加载时重新定义,覆盖默认值。nodePPT 的加载就定义了行内分隔符,不过跟官方定义一样,等于没改。

我猜测,可能早期版本 MathJax 会跟 nodePPT 或 Markdown 的语法冲突,所以改了配置;然后 MathJax 官方也把配置改了,刚好一样。

还是那个经典例子

段模式:

$$x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$$

1
2
3
段模式:
$$x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$$

段模式需要独立一段。

我是行内模式: \(x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}\)

1
我是行内模式: \\(x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}\\)

行内模式则可以跟其他文字混排。

4 实例

接下来提供一系列实例,展示 常见符号 和 公式 用 LaTeX 的写法。

(注:语法均为LaTeX。为简便源码省略环境分隔符。个别字符在 Markdown 环境会被识别为特殊字符,如果跟 Markdown 混排这些字符要加反斜杠 \,如一对下划线 _ 中间的内容会被识别为强调,所以要用\_ 代替。)

重申一遍基本原则:

  • 键盘有的直接输入,不能直接输入的反斜杠 \ + 对应代码
  • 一个字符一个块,效果只对紧接的块起效;多个字符要用 {} 包成一个块

下标

x_1 : \(x_1\)

x_{12} : \(x_{12}\)

特别的是,_强调_ 在 Markdown 里表示 强调 ,如果有连续使用下标,下划线可能会被 Markdown 先识别处理

A_{M-1} < A_{M+1} : \(A{M-1} < A{M+1}\)

可以通过添加反斜杠处理

A\_{M-1} < A\_{M+1} : \(A_{M-1} < A_{M+1}\)

上标

x^2 : \(x^2\)

x^{2a} : \(x^{2a}\)

括号 、 分数

() [] 用原来的就好(花括号{}因为用来分块,所以显示原始花括号要加反斜杠\{\}

也可以用转义,区别就是,如果中间的式子特别大,原始符号不会跟着缩放,但是转义的就会

(\frac{n^2}{6} ) : \(( \frac{n^2}{6} )\)

\left( \frac{n^2}{6} \right) : \(\left( \frac{n^2}{6} \right)\)

根式 、 求和 、 求积 、 积分

\sqrt x : \(\sqrt x\)

\sqrt[3]{x^2+1} : \(\sqrt[3]{x^2+1}\)

\sum_1^n {x^2-1} : \(\sum_1^n {x^2-1}\)

\prod_1^n {x^2-1} : \(\prod_1^n {x^2-1}\)

\int_1^{\frac{\pi}{2}}{sin(x)} : \(\int_1^{\frac{\pi}{2}}{sin(x)}\)

其他特殊符号

\pm \times \div : \(\pm \times \div\)

\lt \gt \le \ge \neq \not\lt : \(\lt \gt \le \ge \neq \not\lt\)

\alpha \beta \gamma : \(\alpha \beta \gamma\)

\infty : \(\infty\)

\forall a \in A : \(\forall a \in A\)

\exists B \subset A : \(\exists B \subset A\)

\underbrace{a+b+ \cdots +z} _{26} : \(\underbrace{a+b+ \cdots +z} _{26}\)

更多参考,见


知识共享 “署名-非商业性使用-相同方式共享” 4.0 (CC BY-NC-SA 4.0)”许可协议
本文为本人原创,采用知识共享 “署名-非商业性使用-相同方式共享” 4.0 (CC BY-NC-SA 4.0)”许可协议进行许可。
本作品可自由复制、传播及基于本作品进行演绎创作。如有以上需要,请留言告知,在文章开头明显位置加上署名(Jayce Chant)、原链接及许可协议信息,并明确指出修改(如有),不得用于商业用途。谢谢合作。
详情请点击查看协议具体内容。