Hidden manipulation of the string from the DOM before it is typeset

52 views
Skip to first unread message

Daniel

unread,
Jan 5, 2017, 7:20:24 AM1/5/17
to MathJax Users
I want MathJax to typset the whole text of every element it receives.

One way to achieve this is to bracket the element's text with

$\text{...}$

before typesetting. It is possible to do this by changing the element's innerHTML, like

element.innerHTML = '$\\text{'+element.innerHTML+'}$'

The downside of this is that the user sees some ugly text before the typesetting.

Is it possible to make the additions to the string withing MathJax before the typesetting without touching the DOM?

A (hopefully) small complication: I have a setting where the user can choose direct math input. If that is set, then the string should only be bracketed with

$...$

So it would be great if one could switch the additions on the fly.

Daniel

Davide Cervone

unread,
Jan 6, 2017, 10:30:52 AM1/6/17
to mathja...@googlegroups.com
One solution would be to not use math delimiters at all, but instead insert the preview and the script tag that MathJax uses for managing the math on the page yourself.  That is, you would replace

<p>math</p>

by

<p><span class="MathJax_Preview">math</span><script type="math/tex">\text{math}</script></p>

This will keep the original math as the preview until the typeset version is shown, and the \text{...} will not appear to the user at all.

Here is an example:

<!DOCTYPE html>
<html>
<head>
<title>MathJax all Nodes</title>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  "fast-preview": {disabled: true},
  styles: {
    ".MathJax_Preview": {color: "inherit"}
  }
});
var nodes = document.getElementsByTagName("p");
for (var i = 0; i < nodes.length; i++) {
  var node = nodes[i];
  var tex = node.textContent;
  node.innerHTML = "";
  MathJax.HTML.addElement(node,"span",{className:"MathJax_Preview"},[tex]);
  var script = MathJax.HTML.addElement(node,"script",{type:"math/tex"});
  MathJax.HTML.setScript(script,"\\text{"+tex+"}");
}
</script>

</head>
<body>

<p>x+1</p>
<p>\sqrt{1-x^2}</p>
<p>\int_0^1 e^x\,dx</p>
<p>This is text</p>

</body>
</html>

I've also disabled the fast preview, so that the preview will remain the original math until the final version is typeset.

I have to say, however, that I don't understand your situation.  Why would you want MathJax to typeset everything using \text{}?  Wouldn't it be better just to leave the text displayed by the browser?  What gain is there on having MathJax render it as text?

Davide


--
You received this message because you are subscribed to the Google Groups "MathJax Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mathjax-user...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Daniel

unread,
Jan 6, 2017, 11:22:59 AM1/6/17
to MathJax Users
Thanks a lot. That seems to work nicely. (Although I was hoping to not have to access the DOM directly but rather just hook something in the MathJaX process.)

As for your question:

1. In the case where math input is set as primary mode of input, every input should be rendered as math. In this case I still want to show the plain input as preview but the whole should be rendered with math and replace the preview once it is done.

2. The idea behind rendering every text as "\text{...}" is to achieve a uniform rendering, e.g. when math is not the primary mode of input in "Test $x_\text{Test}$" the "Test" should appear the same within $$ and outside of it.

Daniel

Davide Cervone

unread,
Jan 6, 2017, 11:59:11 AM1/6/17
to mathja...@googlegroups.com
was hoping to not have to access the DOM directly but rather just hook something in the MathJaX process.

OK, here is a version that encapsulates the DOM manipulation as a MathJax preprocessor, so the Typeset() call will run it automatically.  You just have to be sure that the elements passed to Typeset() are the ones that contain the math directly, and nothing else.  That is why I have set skipStartupTypeset to true, since that initial typeset is on document.body.

<!DOCTYPE html>
<html>
<head>
<title>MathJax all Nodes</title>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  skipStartupTypeset: true,
  "fast-preview": {disabled: true},
  styles: {
    ".MathJax_Preview": {color: "inherit"}
  }
});
MathJax.Extension.myPreprocessor = {
  PreProcess: function (node) {
    var tex = node.textContent;
    node.innerHTML = "";
    MathJax.HTML.addElement(node,"span",{className:"MathJax_Preview"},[tex]);
    var script = MathJax.HTML.addElement(node,"script",{type:"math/tex"});
    MathJax.HTML.setScript(script,"\\text{"+tex+"}");
  }
};
MathJax.Hub.Register.PreProcessor(["PreProcess",MathJax.Extension.myPreprocessor]);
MathJax.Hub.Queue(["Typeset",MathJax.Hub,document.getElementsByTagName("p")]);
</script>

</head>
<body>

<p>x+1</p>
<p>\sqrt{1-x^2}</p>
<p>\int_0^1 e^x\,dx</p>
<p>This is text</p>

</body>
</html>

You will need to change the MathJax.Hub.Queue() call to be the one you need for your elements.


1. In the case where math input is set as primary mode of input, every input should be rendered as math. In this case I still want to show the plain input as preview but the whole should be rendered with math and replace the preview once it is done.

That would be done by leaving off the "\text{"+ and +"}" around the tex variable in the setScript() call.

2. The idea behind rendering every text as "\text{...}" is to achieve a uniform rendering, e.g. when math is not the primary mode of input in "Test $x_\text{Test}$" the "Test" should appear the same within $$ and outside of it.

That would be easier done by setting mtextFontInherit to true in the configuration block for the renderer that you are using.  E.g., 

MathJax.Hub.Config({
  CommonHTML: {mtextFontInherit: true}
});

for this example.  Then you don't have to typeset the text versions.

Davide

Daniel

unread,
Jan 6, 2017, 2:43:10 PM1/6/17
to MathJax Users

Great. This looks like what I was after. Unfortunately, it does not work on my page yet. Basically, what is happening is that all text on the page is typeset even though I queue only certain elements. Also it just moves all the text to the top of the body. I can't figure out what I am doing wrong.

I tried to break my page down to a minimal example. It is a bit tricky, since the page is a bit complicated. I hope I figured it out:


    <!DOCTYPE html>
    <html>
    <head>
    <title>MathJax all Nodes</title>
    <script type="text/x-mathjax-config">
    MathJax.Hub.Config({
        skipStartupTypeset: true,
        jax: [
            'input/TeX',
            'output/CommonHTML'
        ],
        extensions: [
            'tex2jax.js'
        ],
        'fast-preview': {disabled: true}

    });
    MathJax.Extension.myPreprocessor = {
      PreProcess: function (node) {
        var tex = node.textContent;
        node.innerHTML = "";
        MathJax.HTML.addElement(node,"span",{className:"MathJax_Preview"},[tex]);
        var script = MathJax.HTML.addElement(node,"script",{type:"math/tex"});
        MathJax.HTML.setScript(script,tex);
      }
    };
    MathJax.Hub.Register.PreProcessor(["PreProcess",MathJax.Extension.myPreprocessor]);
    MathJax.Hub.Queue(["Typeset",MathJax.Hub,document.getElementById("math")]);
    </script>
    <script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js"></script>


    </head>
    <body>

    <p>x+1</p>
    <p>\sqrt{1-x^2}</p>
    <p>\int_0^1 e^x\,dx</p>
    <p id="math">This is text</p>

    </body>
    </html>

Maybe you see where the mistake is. The basic difference to your code that leads to the problem is (1) only a certain element is typeset, (2) a plain custom config is used.

Daniel

Davide Cervone

unread,
Jan 6, 2017, 2:54:40 PM1/6/17
to mathja...@googlegroups.com
It turns out it is a timing issue with when the document.getElementsById() is performed.  It turns out that the configuration script is being run before the rest of the page is available, and so no element is found, so null is being passed to Typeset(), and that means to typeset the document.body, which is why it is being cleared.

So try replacing

MathJax.Hub.Queue(["Typeset",MathJax.Hub,document.getElementById("math")]);

with

MathJax.Hub.Queue(function () {
  return MathJax.Hub.Typeset(document.getElementById("math"));
});

You can also remove the

extensions: [
    'tex2jax.js'
],

from the configuration, since you are providing your own pre-processing that locates the math.  (Unless you have other math on the page that you also need to typeset.)

Davide


Daniel

unread,
Jan 18, 2017, 3:37:04 PM1/18/17
to MathJax Users
Sorry for the late response. Thanks again for your suggestion.

I replaced all Typeset commands with the function you suggested. Unfortunately, the custom preprocessor still rips my page apart (i.e. just typesets all text on it).

What other kinds changes will I have to do when using this preprocessor. (It is surprising that it seems to work so completely different using the custom preprocessor.)

Davide Cervone

unread,
Feb 15, 2017, 10:36:12 AM2/15/17
to mathja...@googlegroups.com
I replaced all Typeset commands with the function you suggested. Unfortunately, the custom preprocessor still rips my page apart (i.e. just typesets all text on it). 

Hard to tell you what to do without access to the real page involved.  One suggestion would be to use console.log(node) within the PreProcess() function to see what is being passed to it (whether it is the whole page, or just the one node you want.  This might also tell you how often Typeset is being called, which might tell you if typesetting is occurring too often.  Another possibility would be to insert

node = document.getElementById("math");

temporarily at the top of that routine to force it to only do one node and see if that only changes one node.  (Also might want to console.log() the result to make sure there actually IS a node with id="math" at that point.)

In any case, you are going to have to do some debugging to figure out what is happening with your specific page.

What other kinds changes will I have to do when using this preprocessor. (It is surprising that it seems to work so completely different using the custom preprocessor.)

This preprocessor is quite different from the standard ones, which look for delimiters within the nodes to be processed.  This one takes ALL the text of the element.  So you have to be sure the you are calling MathJax.Hub.Typeset() only on the elements whose contents you want processed (and if you are passing an ID as in the examples below, that there really IS an element with the id in the page).

Davide
Reply all
Reply to author
Forward
0 new messages