javascriptcross-browsermathjaxmathml

How to load MathJax only for browsers that do not support MathML


I have a HTML that contains MathML. Now I would like to specify so the browser loads MathJax only if it does not support MathML natively.

Background

As of 2018 autumn, Firefox, Camino, and Safari seem to support MathML, whereas most other browsers, notably Chrome, Internet Explorer, and Opera don't (see Wikipedia's entry of MathML). MathJax kind of works as a workaround to force a browser to interpret MathML, but it has limitation, such as not (yet?) supporting rowspan/colspan (see the issue); besides, MathML prevails in terms of expressing the semantics, which is crucial for non-visual browsers or clients.

So, I would like to load MathJax conditionally — only when the browser does not support MathML.

I should note if MathJax is simply loaded in a blanket way, Firefox, for example, too uses MathJax to re-render MathML, which I want to avoid for the above-mentioned reasons (plus, waste of resources in the client). MathJax should not be loaded (or fired) for Firefox etc.

My measure at the moment

Here is a script as a crude solution I have come up with so Mathjax is loaded if the browser is Chrome (or similar), Konqueror, or MSIE, referring to "Loading MathJax Dynamically" in the Mathjax official document.

<script>
 if (navigator.userAgent.match(/((Chrom(e|ium)|Konqueror)\/?|MSIE \d)/)) {
     var script = document.createElement("script");
     script.type = "text/javascript";
     script.src = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML";
     document.getElementsByTagName("head")[0].appendChild(script);
 }
</script>

But this has some obvious limitations. Does it work for all the versions of the said browsers? There is no guarantee if this condition works on their future versions. Or, those browsers may start supporting MathML in their future versions. Other browsers that do not actually support MathML are not considered, etc.

I suppose a better way, for example, would be to load MathJax only if the browser does not support the <math> tag.

What's the best way to achieve this conditional loading of MathJax? Or, anything better than the code snippet above would be appreciated.


Solution

  • Summary

    Arguably, the best solution is to load a small piece of JavaScript code to find/determine whether the browser can interpret MathML or not and load selectively MathJax JavaScript library according to its assessment.

    Brief historical context

    An excellent starting point for the solution used to be a description in mozilla.org, specifically Section "Fallback". Unfortunately, some of the actual code is outdated and apparently not maintained anymore, perhaps after the shutdown of the cdn server on mathjax.org in 2017. However, a couple of policies presented in it about how to deal with the problem are still good starting points. Here is a brief summary:

    1. For only basic mathematical formulae, loading mathml.css (hosted at https://fred-wang.github.io/mathml.css/mspace.js) can work. A major problem is that it should not be loaded for the browsers that are capable of handling MathML.
    2. Load a small JavaScript to display a warning at the top and let the users choose what to do among a couple of fallback options. The link from the above-mentioned mozilla site does not work well anymore and works only partially at the time of writing.
    3. Load a small JavaScript to selectively load MathJax, only for browsers that do not support MathML.

    Now, I am digging deeper into the third (and I think best) option.

    Solution in detail

    I have published the code to achieve the third strategy (updated from the original code) and more on Github.com and Github.io for direct use. The following is the core of it.

    The algorithm of the original code relies on the visual appearance of the browser's handling of MathML. So, if a future version of Chrome, for example, becomes capable of handling MathML, this code snippet will still work correctly. This does not load MathJax for non-visual web browsers (I think it is the right attitude, for I believe MathJax assumes the visual environment anyway).

    In short, insert the following code in the <head> environment in your HTML and it works, where MathJax Ver.3.2.2 is selectively loaded, depending on the browser. The code is an updated and slightly extended version of the original code (by Frédéric Wang, most likely). As of summer 2022, MathJax is hosted on cloudflare.

    <script>
    (function(){window.addEventListener("load",function(){
      var a,b;
      if(   document.body.getElementsByTagNameNS("http://www.w3.org/1998/Math/MathML","math")[0]
         &&(document.body.insertAdjacentHTML(
            "afterbegin",
            "<div style='border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px;'><math xmlns='http://www.w3.org/1998/Math/MathML'><mpadded height='23px' width='77px'></mpadded></math></div>"
            ),
            b=document.body.firstChild,
            a=b.firstChild.firstChild.getBoundingClientRect(),
            document.body.removeChild(b),
            1 < Math.abs(a.height-23) || 1 < Math.abs(a.width-77)
           )
        ){
        a = document.createElement("script");
        a.src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-chtml.min.js",
        document.head.appendChild(a);
        document.getElementById("q2").innerHTML = "(Chrome-like)";    // Your arbitrary extra code!
      } else {
        document.getElementById("q2").innerHTML = "(Firefox/Safari)"; // Your arbitrary extra code!
      }
     })})();
    </script>
    

    Alternatively, if you prefer MathJax Ver. 2, replace the URI in the code with: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_CHTML

    Or if you prefer, you can use KaTeX or another alternative instead of MathJax. In the above-mentioned Github site I describe pros and cons of several approaches.

    Example MathML/HTML

    Below is an example MathML/HTML code, in which the array mode is also employed. The HTML combined with the above-described JavaScript code generated near-identical visual outputs for Firefox and Chrome (see a screenshot).

    <body>
    <h2>Test <span id="q2"></span></h2>
    <p>
    Using <i>a</i>, the formula
    <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="\sqrt{a}">
      <msqrt>
        <mi>a</mi>
      </msqrt>
    </math>
    is displayed inline.
    </p>
     
    <math xmlns="http://www.w3.org/1998/Math/MathML" alttext="1 = \sqrt{1} = \sqrt{(-1)\times(-1)} \ne \sqrt{-1}\times\sqrt{-1} ">
    <mtable class="m-eqnarray-starred" displaystyle="true" style="display: block; margin-top: 1.0em; margin-bottom: 2.0em">
      <mtr>
        <mtd columnalign="right"> <mn>1</mn> </mtd>
        <mtd columnalign="left">  <mo>=</mo> </mtd>
        <mtd columnalign="left"> <msqrt> <mn>1</mn> </msqrt> </mtd>
      </mtr><mtr>
        <mtd columnalign="right"> </mtd>
        <mtd columnalign="left"> <mo>=</mo> </mtd>
        <mtd columnalign="left"> <msqrt> <mrow>
          <mrow>
            <mo form="prefix">(</mo> <mo>-</mo> <mn>1</mn> <mo form="postfix">)</mo>
          </mrow>
          <mo>&#x000D7;</mo>
          <mrow>
            <mo form="prefix">(</mo> <mo>-</mo> <mn>1</mn> <mo form="postfix">)</mo>
          </mrow>
        </mrow> </msqrt> </mtd>
      </mtr> <mtr>
        <mtd columnalign="right"> </mtd>
        <mtd columnalign="left"> <mo>≠</mo> </mtd>
        <mtd columnalign="left">
          <msqrt> <mo>-</mo> <mn>1</mn> </msqrt>
          <mo>&#x000D7;</mo>
          <msqrt> <mo>-</mo> <mn>1</mn> </msqrt>
        </mtd>
      </mtr>
    </mtable>
    </math>
    </body>
    

    Comment on assessment based on User-Agent

    Finally, let me give a brief comment about algorithms to load selectively MathJax based on User-Agent. Here is an example JavaScript code for it.

      if (navigator.userAgent.match(
            /((Chrom(e|ium)|Konqueror|Edge?|Opera|OPR)\/?|MSIE \d)/)) { // ...
      }
    

    A major trouble is that Chrome sends a similar User-Agent to Firefox in default, which is highly confusing. So, I don't think an assessment based on User-Agent is a good idea, apart from the fact such a strategy in principle requires a constant maintenance/update of the script by both developers and users, considering a future version of a browser may anytime become capable of handling the MathML.