野声

Hey, 野声!

谁有天大力气可以拎着自己飞呀
twitter
github

Dynamically load Mathjax in React project.

When doing a React project, I want to render mathematical formulas on a webpage without using pre-packaged React components. I want to directly render the DOM using dynamic loading.

Mathjax@3 has made a major update, and the usage is different from version 2.x.

Dynamically Loading JS in React#

The first thing to know is how to dynamically load JS. You can mount a new script node to the DOM using JavaScript.

Create a script element, set the src attribute to the URL you want to load, and then append the node to the body element.

Here is a function that encapsulates this process. You just need to pass the URL of the JS file you want to load and use the .then method for subsequent operations.

export const loadJS = (url: string) =>
  new Promise(function (resolve, reject) {
    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = url;
    document.body.appendChild(script);
    script.onload = function () {
      resolve(`success: ${url}`);
    };
    script.onerror = function () {
      reject(Error(`${url} load error!`));
    };
  });

In the place where you need to use it:

loadJS(url)
  .then(() => {
    // do something
  })
  .catch(() => {
    // do something
  });

[email protected] Version#

First, here are some official reference links. This article may not be very clear.

After loading Mathjax, we just need to render the DOM at the appropriate time.

Here's an example:

componentDidMount() {
  const mathjaxUrl = 'https://cdn.bootcss.com/mathjax/2.7.4/MathJax.js?config=TeX-AMS_CHTML';
  loadJS(mathjaxUrl).then(() => {
    this.showMathjax();
  });
}

componentDidUpdate () {
  if (!(window as any).MathJax) {
    (window as any).MathJax.Hub.Queue(['Typeset', (window as any).MathJax.Hub, ReactDOM.findDOMNode(this)]);
  }
}

showMathjax = () => {
  if ((window as any).MathJax) {
    (window as any).MathJax.Hub.Config({
      tex2jax: {
        inlineMath: [['$', '$']],
        displayMath: [['$$', '$$']],
        skipTags: ['script', 'noscript', 'style', 'textarea', 'code', 'a'],
      },
      CommonHTML: {
        scale: 120,
        linebreaks: { automatic: true },
      },
      'HTML-CSS': { linebreaks: { automatic: true } },
      SVG: { linebreaks: { automatic: true } },
      TeX: { noErrors: { disabled: true } },
    });
  } else {
    setTimeout(this.showMathjax, 1000);
  }
};

In version 2.x, we need to use Mathjax.Hub.Config for configuration. The configurations are quite easy to understand. In tex2jax, we set how to render mathematical formulas when they are wrapped in certain characters.

Inline formulas are wrapped in $...$, and multiline formulas are wrapped in $$...$$. We also specify which tags should not be rendered.
Then we set the rendering behavior for different modes. The JS file we loaded has a query ?config=TeX-AMS_CHTML, which indicates that we loaded the CHTML configuration. Different configurations have different display effects. See https://docs.mathjax.org/en/v2.7-latest/config-files.html

After loading Mathjax, it will automatically render the page. However, for single-page applications, Mathjax will not re-render when the DOM is updated. We need to use MathJax.Hub.Queue(['Typeset', MathJax.Hub, ReactDOM.findDOMNode(this)]); to manually render the DOM. Add it to componentDidUpdate.

Mathjax@3 Version#

Here are the official documentation links:

In version 3, the loading and configuration methods of Mathjax are different.

Upgrading from v2 to v3: http://docs.mathjax.org/en/latest/upgrading/v2.html

You can directly load JS files with different configurations without using ?config=xxx. For example, load https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js.

The Mathjax.Hub methods have been removed. Now, you just need to assign a configuration object to window.Mathjax to set the configuration.

The official documentation also provides a link to convert configurations: MathJax Configuration Converter

Here is my configuration:

(window as any).MathJax = {
  tex: {
    inlineMath: [['$', '$']],
    displayMath: [['$$', '$$']],
  },
  options: {
    skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'code', 'a'],
  },
  chtml: {
    scale: 1.2,
  },
  startup: {
    ready: () => {
      (window as any).MathJax.startup.defaultReady();
      (window as any).MathJax.startup.promise.then(() => {
        console.log('MathJax initial typesetting complete');
      });
    },
  },
};

After the Mathjax script is loaded, it reads the window.Mathjax as the configuration and replaces it with the Mathjax object, so you can call related functions.

When Mathjax is initialized, it calls the startup.ready method in the configuration, where you can provide prompts or other configurations.

The same problem exists: Mathjax does not re-render when the DOM is updated. You need to use the MathJax.typesetPromise() method. Set it in componentDidUpdate.

componentDidUpdate() {
  const MathJax = (window as any).MathJax;
  if (MathJax) {
    MathJax.typesetPromise && MathJax.typesetPromise();
  }
}

This method is asynchronous. There is also a synchronous method with the same functionality: MathJax.typeset().

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.