Making MathJax Formulas Auto-Scale with Window Size

With the emergence and popularity of MathJax, displaying mathematical formulas on web pages has more or less become standardized. However, MathJax (along with its competitor KaTeX) is only responsible for converting LaTeX code into rendered formulas on the page — it still doesn't offer a good solution for adaptive resolution. On some of the math-heavy articles on this site, for instance, since the layout was designed on PC, it looks fine when browsed on a desktop, but can become quite unpleasant to look at once you switch to a phone.

After some testing, I've come up with a solution that lets MathJax formulas scale automatically with window size, much like images do, so as to preserve the display quality on mobile devices as much as possible. I'd like to share it here.

Background and Motivation

This problem originates from the fact that, even when laying things out on PC, one sometimes runs into a single-line formula whose length exceeds the width of the page but that isn't easy to break across lines. One workaround is to manually adjust the font size of the formula using HTML, for example:

<span style="font-size:90%">
    \begin{equation}一个超长的数学公式\end{equation}
</span>

This scales the formula down to 90% of its original size, which solves most cases. But manual adjustment is ultimately a hassle — the value 90% needs to be tuned by hand through trial and error to get an optimal result, and it only works for a single specific width. A few days ago it suddenly occurred to me: why can't a math formula have a max-width set on it and auto-scale, just like an image? For example, with the following image code:

<img style="width:400;max-width:100%" src="https://example.com/test.jpg">

the effect achieved is that the image size stays as close to 400px as possible without exceeding the width of its parent element. After some testing, I found that MathJax formulas are rendered as text blocks, and there's no built-in way to give them an image-like max-width scaling behavior. However, we can use JavaScript to compute the ratio by which a formula's width exceeds its parent element's width, and then automatically set the font-size on the formula to achieve the same effect.

Reference Code

Let me first give the final reference solution: replace the original MathJax configuration code with the following:

<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
        tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]},
        TeX: {equationNumbers: {autoNumber: ["AMS"], useLabelIds: true}, extensions: ["AMSmath.js", "AMSsymbols.js", "extpfeil.js"]},
        "HTML-CSS": {noReflows: false, availableFonts: ["tex"], styles: {".MathJax_Display": {margin: "1em 0em 0.7em;", display: "inline-block!important;"}}},
        "CommonHTML": {noReflows: false, availableFonts: ["tex"], styles: {".MJXc-display": {margin: "1em 0em 0.7em;", display: "inline-block!important;"}}},
        "SVG": {noReflows: false, availableFonts: ["tex"], styles: {".MathJax_SVG_Display": {margin: "1em 0em 0.7em;", display: "inline-block!important;"}}}
    });
    MathJax.Hub.Queue(function() {
        document.querySelectorAll('span[id^="MathJax-Element"]').forEach(function(e) {
            parentWidth = e.parentNode.offsetWidth;
            if (e.parentNode.className.endsWith('isplay')) {
                parentWidth = e.parentNode.parentNode.offsetWidth;
            }
            if (e.offsetWidth > parentWidth) {
                e.style.fontSize = parentWidth * 100 / e.offsetWidth + '%';
            }
        });
    });
</script>

Compared with the configuration code mentioned in Making MathJax Better Compatible with Google Translate and Lazy Loading, there are two key changes here. First, the configuration option {linebreaks: {automatic: true, width: "95% container"} has been removed. This option controls automatic line-wrapping, and its original intent was to improve adaptability to different resolutions. In practice, though, it's not very useful — many carefully composed formulas actually look worse when auto-wrapped. So removing auto-wrapping is necessary to keep formulas visually consistent across different widths.

The other key change is the MathJax.Hub.Queue part, which is a function executed after formula rendering is complete. It first finds all spans whose id begins with "MathJax-Element" — these are all the math formulas — then gets each one's width via e.offsetWidth, and gets its parent element's width via either e.parentNode.offsetWidth or e.parentNode.parentNode.offsetWidth. Based on this, it computes and applies the scaling ratio to shrink the font-size accordingly.

When should we use e.parentNode.offsetWidth versus e.parentNode.parentNode.offsetWidth? Formulas come in two flavors: "inline formulas" and "display (single-line) formulas." For inline formulas we use the former, and for display formulas we use the latter. In terms of DOM structure, display formulas have an extra nested div, so it's the grandparent node that serves as the "parent element" in this sense — and since that div's class name ends with "Display" or "display", I added the condition e.parentNode.className.endsWith('isplay') to distinguish the two cases.

That covers the entire logic behind the reference code above.

Summary

This post shared a method for making MathJax formulas automatically scale with window size, in order to accommodate narrow-screen browsing on mobile devices as much as possible. After this adjustment, even on small screens you can now see the same math formulas as on PC — though it might strain your eyes a little, it's certainly good enough as a stopgap solution.

Narrow-screen display after the fixNarrow-screen display after the fix

English translation of a post from 科学空间 | Scientific Spaces by 苏剑林. Original: https://kexue.fm/archives/10474
Translated automatically with claude-sonnet-5; all equations are reproduced verbatim from the source. Copyright remains with the original author.