Dynamically Redefine MathJax Macros

73 views
Skip to first unread message

Arthur Lovekin

unread,
Oct 5, 2024, 4:59:06 PM10/5/24
to MathJax Users
Hi all, I am wondering if it is possible to dynamically redefine MathJax macros.
The scenario I have in mind is that I would like the user to be able to change the naming convention used throughout my document by selecting from a dropdown. For example, in the article text I would write $$\state_{\discreteTime} = \state-transition \state_{discreteTime-1}$$ and then the user would see x_t=Fx_t-1 or s_n=As_n-1 depending on which naming convention they've selected. I would implement this by simply redefining the mathjax macros for \state and \stateTransition, and then re-typesetting the entire document.

I've attached my attempt at how I think this should work.

One very tedious solution would be to simply define each formula in Javascript, then include in the HTML <span class="specific-formula"><\span> tags for each individual formula I want. However, this is prohibitively tedious because I have everything from inline equations to long block derivations to single variables (each one with many possible variations of subscripts, hats, etc). My article also uses dynamic tooltip and highlighting elements, and it would be easier to tie a (possibly different) class/id to each element depending on the naming convention (eg. `\\def\\state{{\\class{highlightBlue}{\\mathbf{s}}}}`)
dynamic_mathjax_macros.html

Davide Cervone

unread,
Oct 6, 2024, 7:53:30 AM10/6/24
to mathja...@googlegroups.com
Here is how I would handle your situation:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dynamic MathJax Macros</title>
    <script>
    MathJax = {
      TexMacros: {
        alt0: `
          \\def\\state{\\mathbf{x}}
          \\def\\stateTransition{\\mathbf{F}}
          \\def\\discreteTime{n}
        `,
        alt1: `
          \\def\\state{\\mathbf{s}}
          \\def\\stateTransition{\\mathbf{A}}
          \\def\\discreteTime{t}
        `
      },
      setMacros(name) {
        return MathJax.startup.promise = MathJax.startup.promise.then(() => {
          const {mathjax} = MathJax._.mathjax;
          const {STATE} = MathJax._.core.MathItem;
          MathJax.tex2mml(MathJax.config.TexMacros[name]);
          mathjax.handleRetriesFor(() => MathJax.startup.document.rerender(STATE.COMPILED));
        });
      },
      startup: {
        typeset: false,
        ready() {
          MathJax.startup.defaultReady();
          MathJax.config.setMacros('alt0');
          const dropdown = document.getElementById('naming-convention-dropdown');
          dropdown.addEventListener('input', (event) => MathJax.config.setMacros(event.target.value));
        }
      }
    }
    </script>
    <script id="MathJax-script" async
    </script>
</head>
<body>

    <h1>Dynamic MathJax Macros</h1>

    <select id="naming-convention-dropdown">
        <option value="alt0">Alt0</option>
        <option value="alt1">Alt1</option>
    </select>

    <p>Test inline: \( \state_{\discreteTime} = \stateTransition \state_{\discreteTime-1} \),
       and block: $$ \state_{\discreteTime} = \stateTransition \state_{\discreteTime-1} $$</p>

</body>
</html>

I've stored the functions and data needed for this in the MathJax configuration object (which becomes the MathJax.config object after MathJax is loaded) so as to reduce the number of global variables and functions.  I store the macro definitions in an object with keys given by the values of your alternatives in the popup menu, so that we can use the same call to define either one.  A setMacros() function switches sets (using MathJax.tex2mml(), which processes the TeX without it needing to be in the page) and re-renders the page.  This is the key piece you were missing, since MathJax.typesetPromise() doesn't re-render any existing expressions.  We ask the rendering to go back to the compile step (that converts LaTeX into the internal MathML), which is how your new macros get used.  I've disabled the initial typesetting pass via setup.typeset in the configuration, since we do the typeset in the setMacros() function that is called in the startup ready() function.

Finally, I simplified your macros a bit, as the extra braces were not necessary, there was an extra backslash in the alt0 set, and the use of \it was unnecessary (and incorrect, since it gets you text italics, which aren't the same as math italics).

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.
To view this discussion on the web visit https://groups.google.com/d/msgid/mathjax-users/e966f7fa-b996-4954-8148-c7c407b36d08n%40googlegroups.com.
<dynamic_mathjax_macros.html>

Arthur Lovekin

unread,
Oct 6, 2024, 9:48:31 PM10/6/24
to MathJax Users
Many thanks! I've been banging my head on this and I never would have been able to work with the guts of the pre-initialized MathJax object the way you did. When I finish the article I'm working on I will send it your way.

Arthur

Davide Cervone

unread,
Oct 8, 2024, 3:58:59 PM10/8/24
to mathja...@googlegroups.com
Glad that worked for you.  Looking forward to seeing your article.

Davide


Arthur Lovekin

unread,
Nov 12, 2024, 6:28:25 PM11/12/24
to MathJax Users
Davide, thank you again for your help. At long last my finished article is here: https://arthurlovekin.com/interactive-kalman-filter/
Reply all
Reply to author
Forward
Message has been deleted
0 new messages