本文由
AI
辅助撰写,可能存在不准确之处,请读者注意甄别!
在文章《让MathJax更好地兼容谷歌翻译和延时加载》中,原作者探讨了Cool Papers引入MathJax解析LaTeX公式后的兼容性问题。虽已解决部分问题,但在Cool Papers中,MathJax与Markdown渲染库Marked之间的冲突依然存在。这篇文章将专注于如何完美解决这个问题。
问题背景
Markdown是一种轻量级的标记语言,广泛用于编写文档。Cool Papers使用的Kimi功能输出的是Markdown语法,并通过Marked库在前端将其转换为HTML。然而,LaTeX公式与Markdown有部分语法重合,这导致Marked可能会错误处理LaTeX代码,导致MathJax无法正确渲染公式。
一个简单的代码展示了问题所在:
<div id="content"></div>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.9/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
var div = document.getElementById('content');
div.innerHTML = marked.parse('**cannot** render: \\(a^2 + b^2\\), **can** render: \\\\(c^2 + d^2\\\\)');
MathJax.Hub.Typeset(div);
</script>
在这段代码中,\\(a^2 + b^2\\)
和\\\\(c^2 + d^2\\\\)
代表LaTeX公式,但只有后者能被MathJax正确渲染。原因是Marked误解析了部分LaTeX代码,导致公式失效。
已有解决方案
目前,社区已经提出了几种解决MathJax与Marked冲突的方法:
- 手动转义:通过手动修改LaTeX代码以适应Marked的解析方式,虽然有效但不直观,且Cool Papers使用的公式是自动生成的,不能轻易修改。
- 保护公式:将LaTeX代码放在代码块中避免被Marked解析,但这种方式容易与实际的代码块混淆。
- 更换引擎:使用更好的渲染引擎,如Pandoc,但这种方案主要针对后端,前端渲染没有很好的替代方案。
- 修改Marked引擎:修改Marked的规则,避免其解析LaTeX代码。但这种方式治标不治本,需要不断手动调整规则。
这些方案都存在一定的局限性,因此我们需要一个更加优雅的解决方案。
逆向思路:先MathJax,后Marked
问题的根源在于Marked与MathJax的渲染顺序——Marked会误解析LaTeX代码。那么,如果我们反过来处理,先用MathJax渲染公式,再用Marked处理Markdown内容,这样便可以从根本上解决问题。
以下是实现该思路的代码:
<div id="content"></div>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.9/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
var div = document.getElementById('content');
div.innerHTML = '**can** render: \\(a^2 + b^2\\)';
MathJax.Hub.Queue(
['Typeset', MathJax.Hub, div],
function() {
div.innerHTML = marked.parse(div.innerHTML);
}
);
</script>
该方案能够让MathJax优先处理LaTeX公式,确保公式能够被正确解析,然后再由Marked处理剩余的Markdown内容。
进一步优化
尽管上述方案能够有效解决问题,但对于有“强迫症”的用户来说,仍有两个小瑕疵:
1. 初始页面显示乱码
在渲染完成之前,用户会短暂看到未处理的Markdown文本,类似乱码。为了改善这一体验,可以在渲染完成后再将内容显示给用户:
<div id="content"></div>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.9/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
var div = document.getElementById('content');
var div2 = document.createElement('div');
div2.innerHTML = '**can** render: \\(a^2 + b^2\\)';
MathJax.Hub.Queue(
['Typeset', MathJax.Hub, div2],
function() {
div.innerHTML = marked.parse(div2.innerHTML);
}
);
</script>
通过在隐藏的元素中进行渲染,用户可以直接看到最终渲染结果,避免看到中间的乱码。
2. MathJax菜单消失
另一个问题是,MathJax自带的右键菜单功能消失了。原因是,重新渲染HTML内容时,绑定在公式上的事件监听器被移除了。为了解决这个问题,可以在Markdown解析后,重新调用MathJax的Typeset
方法来渲染公式:
<div id="content"></div>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.9/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
var div = document.getElementById('content');
var div2 = document.createElement('div');
div2.innerHTML = '**can** render: \\(a^2 + b^2\\)';
MathJax.Hub.Queue(
['Typeset', MathJax.Hub, div2],
function() {
div.innerHTML = marked.parse(div2.innerHTML);
div.querySelectorAll('.MathJax').forEach(e => e.remove());
MathJax.Hub.Typeset(div);
}
);
</script>
在此代码中,删除已渲染的公式并重新调用Typeset
确保MathJax功能正常。
3. 保存原始公式代码
Marked会错误解析嵌入的<script>
标签,导致MathJax的渲染再次出错。为此,我们可以在marked.parse
之前保存公式代码,渲染后再覆盖回去:
<div id="content"></div>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.9/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
function parseMarkdown(text) {
var scripts = text.match(/<script[^>]*>([\s\S]*?)<\/script>/gi);
text = marked.parse(text);
return text.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, m => scripts.shift());
}
var div = document.getElementById('content');
var div2 = document.createElement('div');
div2.innerHTML = '**can** render: \\(J\'_\\theta = J_\\theta\\)';
MathJax.Hub.Queue(
['Typeset', MathJax.Hub, div2],
function() {
div.innerHTML = parseMarkdown(div2.innerHTML);
div.querySelectorAll('.MathJax').forEach(e => e.remove());
MathJax.Hub.Typeset(div);
}
);
</script>
通过这种方式,我们能够避免Marked对公式进行错误解析,同时保证MathJax的正常渲染。
结论
通过逆向思路,先用MathJax处理公式,再用Marked渲染Markdown,我们可以有效解决MathJax与Marked的冲突问题,并进一步通过优化,提升页面的显示效果和用户体验。这种方法不仅适用于前端渲染的场景,也可以为后端渲染提供参考。
参考资料:
近乎完美地解决MathJax与Marked的冲突 - 科学空间|Scientific Spaces
给自己的站点增加基于MathJax的LaTeX公式支持 - 知乎
版权属于:soarli
本文链接:https://blog.soarli.top/archives/729.html
转载时须注明出处及本声明。