工具基础
react + nextjs 项目
notebookjs 获取 notebook 的 outer html
katex 解析公式
问题描述
在线展示 .ipynb 文件的内容,在 markdown 的 cell 中出现部分数学公式未正确解析成功
如图所示
可以看到最后两行未被解析成数学公式
分析问题
已知 katex 在解析过程中进行了规则配置,通过引入 katex 中的
实现方式如下
import renderMathInElement from 'katex/contrib/auto-render';
renderMathInElement(document.body, {
// custom options
delimiters: [
{ left: '$$', right: '$$', display: true },
{ left: '$', right: '$', display: false },
{ left: '\\(', right: '\\)', display: false },
{ left: '\\[', right: '\\]', display: true },
],
throwOnError: false,
});
可以看到这其中是缺少了 \begin{align}
\end{align}
这对标记的解析
解决问题
所以需要增加规则的配置
下面是一个比较完整的版本
renderMathInElement(document.body, {
// custom options
// • auto-render specific keys, e.g.:
delimiters: [
{ left: '$$', right: '$$', display: true },
{ left: '$', right: '$', display: false },
{ left: '\\(', right: '\\)', display: false },
{ left: '\\[', right: '\\]', display: true },
{
left: '\\begin{equation}',
right: '\\end{equation}',
display: true,
},
{ left: '\\begin{align}', right: '\\end{align}', display: true },
{ left: '\\begin{alignat}', right: '\\end{alignat}', display: true },
{ left: '\\begin{gather}', right: '\\end{gather}', display: true },
{ left: '\\begin{CD}', right: '\\end{CD}', display: true },
],
// • rendering keys, e.g.:
throwOnError: false,
});
在增加了这些规则后,发现仍旧有部分数学公式未被正确解析,查看源代码
由于 p 元素中包含了 em 元素,导致 katex 未解析这段代码。
通过对比接口的数据发现,源数据中并未包含 em 元素,源数据 "\\begin{align}{TD}_t = r + \\gamma Q_{target}^*(s',a')\\end{align}\n"
那么 em 元素是哪儿来的呢?
原来从接口拿到数据后,调用了 notebookjs 提供的 parse 方法和 render 方法,获取到 outerHtml。然后再把这段 html 放到页面展示。
那么接下来就是修复 notebook 的解析问题
尝试解决
方法一 元素逆向替换
由于 markdown 语法转换成 html 后多出来了 em 标签,那么反过来我把这段 htmlstring 中的 em 标签手动替换回 _ 是不是就可以?
说干就干
const replaceEmTags=(htmlString)=> {
const div = document.createElement('div');
div.innerHTML = htmlString;
const markdownCells = div.getElementsByClassName('nb-markdown-cell');
for (let i = 0; i < markdownCells.length; i++) {
const pElements = markdownCells[i].getElementsByTagName('p');
for (let j = 0; j < pElements.length; j++) {
const pElement = pElements[j];
pElement.innerHTML = pElement.innerHTML.replace(/<em>/g, '_').replace(/<\/em>/g, '_');
}
}
return div.innerHTML;
}
转换后发现确实有一点作用,但同时在 markdown 中使用强调效果的文字就会消失。
且发现 这个转换会把 - 转换为 ul li 的列表,这下换成两个标签的嵌套,显然上面的替换方法是行不通的。