【问题标题】:MathJax or similar: render arbitrary HTML element inside expression?MathJax 或类似的:在表达式中呈现任意 HTML 元素?
【发布时间】:2020-03-11 23:05:53
【问题描述】:

我对 Web 应用程序中的数学表达式呈现有特殊需求,虽然到目前为止我主要关注的是 MathJax,但我当然不喜欢它。

我需要的是能够呈现一个数学表达式,其中一个或多个术语本质上可以是任意 HTML 框元素,如下所示:

...如果布局响应 HTML 框的大小(例如括号会自动调整大小以匹配其高度,就像任何其他“正常”LaTeX/MathJax 元素一样),那将是更可取的,但如果我需要以像素为单位指定一个确切的大小或类似的东西,那也可以。如果我不得不这样做,甚至可以。在实际可视化中插入一个“占位符”元素,但可以准确地知道它在输出中的位置,因此我可以将 HTML 元素精确地覆盖在它上面。

换句话说,我对开箱即用甚至是骇人听闻的解决方案持开放态度;只需要一些有用的东西。

也可能相关:我真的只会使用非常基本的 LaTeX 元素:括号、分数、普通运算符。如果这是一个复杂的因素,我什至不需要能够支持上述示例中的求和。

【问题讨论】:

  • @DavideCervone 很抱歉回复晚了——我专注于另一个项目,今天早上之前没有时间真正消化你的答案。我认为这将非常完美!感谢您的回答。
  • 感谢您提供赏金。我希望其中一种方法对您有用。

标签: html latex mathjax mathml


【解决方案1】:

有几种方法可以解决这个问题。您插入占位符并覆盖它的建议是一个,可以按如下方式完成:

<script type="text/x-mathjax-config">
MathJax.Hub.Queue(function () {
  var PX = function (x) {return x.toFixed(2) + 'px'};
  var space = document.getElementById('space');
  var sbox = space.getBoundingClientRect();
  var math = MathJax.Hub.getJaxFor(space).SourceElement().previousSibling;
  var mbox = math.getBoundingClientRect();
  var x = sbox.left - mbox.left;
  var y = sbox.top - mbox.top;
  var html = MathJax.HTML.Element('div', {id:'html', style: {
    position:'absolute', top:PX(y), left:PX(x), 
    width:PX(sbox.width), height:PX(sbox.height), 'line-height':'normal'
  }}, [[
    'table',{style:{height:'100%', width:'100%', background:'red', border:'5px solid green'}}, [[
      'tr',{},[[
        'td',{},['abc']
      ]]
    ]]
  ]]);
  math.style.position = 'relative';
  math.appendChild(html);
});
</script>
<script id="MathJax-script" src="https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js?config=TeX-AMS_CHTML" defer></script>

<div style="xfont-size: 150%">
$$x + \left(\,\cssId{space}{\Space{100px}{55px}{45px}}\,\right) + y$$
</div>

这里我们使用\Space 插入所需宽度、高度和深度的空白区域,并使用\cssId 标记空间以便我们稍后检索。我们使用MathJax.Hub.Queue() 对函数进行排队,以便在 MathJax 排版数学后运行。此函数查找空间持有者元素并找到 MathJax 用来保存数学的容器元素。它获取每个边界框的信息,并计算叠加层相对于数学的位置。然后它创建了覆盖层(在本例中,内容是硬编码的),具有适当的大小和药水以覆盖在空间持有人的顶部。

这可行,但有点尴尬,对于显示的方程式,如果窗口大小发生变化,叠加层可能不会停留在正确的位置。此外,它仅适用于 CommonHTML 输出。


下面是另一种选择,它使用&lt;semantics&gt; 元素将HTML 包含到MathJax 使用的内部MathML 结构中。它添加了一个新的宏 \insertHTML{} 来完成它。

<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  CommonHTML: {
    styles: {
      //
      // remove CSS for '.mjx-math *'
      //
      '.mjx-math *': {
        display: null,
        '-webkit-box-sizing': null,
        '-moz-box-sizing': null,
        'box-sizing': null,
        'tex-align': null
      },
      //
      // add CSS for .mjx-math span instead
      //
      '.mjx-math span': {
        display: 'inline-block',
        '-webkit-box-sizing': 'context-box !important',
        '-moz-box-sizing': 'context-box !important',
        'box-sizing': 'context-box !important',
        'tex-align': 'left'
      },
      //
      // override display for .mjx-char spans
      //
      'span.mjx-char': {
        display: 'block'
      }
    }
  }
});
MathJax.Hub.Register.StartupHook("TeX Jax Ready", function () {
  var MML = MathJax.ElementJax.mml;
  var TEX = MathJax.InputJax.TeX;
  TEX.Definitions.macros.insertHTML = 'InsertHTML';
  TEX.Parse.Augment({
    InsertHTML: function (name) {
      var html = this.GetArgument(name).replace(/^\s*<!--\s*/,'').replace(/\s*-->\s*$/,'');
      var span = MathJax.HTML.Element('mjx-reset', {style: {display:'inline-block'}});
      span.innerHTML = html;  // serious security risk if users can enter math
      span.setAttribute("xmlns","http://www.w3.org/1999/xhtml");
      var mml = MML["annotation-xml"](MML.xml(span)).With({encoding:"application/xhtml+xml",isToken:true});
      this.Push(MML.semantics(mml));
    }
  });
});
</script>
<script id="MathJax-script" src="https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js?config=TeX-AMS_CHTML" defer></script>

<div style="xfont-size: 150%">
$$x + \left(\,\insertHTML{<!--
<table width="100" height="100"
  style="display:inline-table; vertical-align:-.25em; background:red; border:5px solid green;
  box-sizing:border-box !important">
<tr><td style="text-align:center">abc</td></tr>
</table>
-->}\,\right) + y$$
</div>


$$x+\left(\insertHTML{<!--
<i>this</i> is <b>html</b>
-->}\right)+y$$

因为 MathJax 不会直接处理包含 HTML 标记的数学运算,所以您必须将 HTML 放在注释中,您可以在上面的两个示例表达式中看到。这个版本适用于所有输出格式(甚至是 MathML 输出),但它确实需要一些配置来覆盖一些适用于内容的 CommonHTML CSS,因为它包含在 MathJax 布局的内部。

在这种情况下,无论窗口发生什么,HTML 都会保持正确的位置,并且它的优点是允许在表达式本身中给出 HTML。但是请注意,如果您的网站允许用户输入数学,这会带来安全风险,因为这意味着他们可以在您的页面中输入任意 HTML。在这种情况下,您可能需要在 InsertHTML 函数中设置 innerHTML 之前清理 HTML。

【讨论】:

  • 关于第二种方法的问题:在 Mac 上使用 Chrome 时,第一个示例的垂直尺寸是关闭的——具体来说,括号比红色 HTML 元素高约 40%。它在 Safari 上的大小合适;没有检查其他浏览器。知道是什么原因造成的吗?谢谢!
  • ...进行了更多测试后,当 HTML 元素是单行文本的高度时,它的大小看起来很合适,但是(据我观察)看起来对于超出这一行文本的每个额外高度的像素,括号的高度都会增加 2 个像素,HTML 元素与括号的底部对齐。
  • @DanM,Chrome 似乎没有实现 revert CSS,我用它来撤消 CommonHTML 输出渲染器使用的一些 CSS。这导致表格元素没有正确的表格display 属性,因此包含abc 的单元格没有正确居中。这使得表格的大部分高度变成了包含表格的行的深度。因为括号是对称的并且必须覆盖那么大的深度,所以它们也变得特别高(这是正确处理显示的表格的括号......
  • ... 所以 CSS 重置不起作用,必须以不同的方式处理。我将修改上面的示例以更改 CSS 而不是重置它,这应该可以解决 Chrome 中的问题。反正这样更好。
猜你喜欢
  • 2018-02-14
  • 1970-01-01
  • 1970-01-01
  • 2013-04-05
  • 1970-01-01
  • 1970-01-01
  • 2014-02-05
  • 1970-01-01
  • 2020-04-11
相关资源
最近更新 更多