MathJax in Dynamic Content — MathJax 4.0 documentation (original) (raw)

When MathJax is initially loaded, it will typeset any math that is available in the page. For static pages, that is all that you will need to do to make your mathematics appear and be accessible. Many pages, however, load or create content dynamically, and if that new material contains mathematics, MathJax will need to be told to typeset that new mathematics, as that is not done automatically by MathJax.

The following sections show how this can be done within theMathJax Components framework using the commands that are part of the MathJax global variable. For node applications or custom builds of MathJax that don’t use MathJax Components, you would need to use the lower-level direct calls to the MathJaxMathDocument methods that correspond to those commands.

Handling New Content

If you are writing a dynamic web page where content containing mathematics may appear after MathJax has already typeset the rest of the page, then you will need to tell MathJax to look for mathematics in the page again when that new content is produced. To do that, you need to use one of the MathJax.typeset() orMathJax.typesetPromise() functions. These cause MathJax to look for unprocessed mathematics on the page and typeset it, leaving unchanged any math that has already been typeset.

The first of these runs synchronously, but if the mathematics on the page uses \require or causes an extension to be auto-loaded (via the autoload component), or needs characters from a region of the font that hasn’t already been loaded, this will cause theMathJax.typeset() call to fail. In this case, you should use MathJax.typesetPromise() instead. This returns a promise that is resolves when the typesetting is complete. See theHandling Asynchronous Typesetting section for more details.

Note

The MathJax.typeset() command corresponds to

(elements = null) => { const doc = MathJax.startup.document; doc.options.elements = elements; doc.reset(); doc.render(); }

and MathJax.typesetPromise() corresponds to

(elements = null) => { const doc = MathJax.startup.document; return doc.whenReady(async () => { doc.options.elements = elements; doc.reset(); await doc.renderPromise(); }); }

Those not using MathJax Components can use these definitions, whereconst doc = ... is replaced by your own MathDocumentobject created by mathjax.document().

Handling Content that Changes

Some web pages replace old content with new content in some circumstances. For example, a “book reader” may load individual pages to be displayed in its main content area, with each new page replacing the previously viewed one, or a page that allows users to enter content may include an editor with a preview that updates as the user types new content.

When the changing content includes typeset mathematics, special care must be taken to inform MathJax that previously typeset mathematics is being removed before doing so. This is because MathJax keeps track of the expressions that it typesets so that they can be updated if changes are made to the menu settings. For example, if the renderer is changed, MathJax needs to go back and re-render all the expressions, so it needs to know where they are in the page.

Information about the mathematics that MathJax has typeset is stored in a list of MathItem objects that is part of theMathDocument maintained by MathJax. If content containing typeset mathematics is removed from the page, the correspondingMathItem objects need to be removed from that list, otherwise MathJax will think that math is still in the page, which can lead to problems if MathJax tries to re-render those items. It also means that the list can grow unexpectedly large, and that the old typeset expressions are not freed from memory, causing MathJax’s memory usage to grow. In a situation like an editor with preview, where the content is updated for each keystroke, that can lead to a rapid growth in memory and a corresponding decrease in performance over time.

To deal with changing content, MathJax provides a function that tells it to forget about math that it has previously typeset:

MathJax.typsetClear(elements)

Arguments:

If you are removing a portion of your document that may include typeset mathematics, you should call this function before removing the content from the DOM so that MathJax can determine which expressions it contains and remove them from its internal list of expressions. If you fail to do this, MathJax’s expression list will contain orphan expressions that are no longer part of the DOM.

Note

The MathJax.typesetClear() method corresponds to

(elements = null) => { const doc = MathJax.startup.document; if (elements) { doc.clearMathItemsWithin(elements); } else { doc.clear(); } }

Those not using MathJax Components can use these definitions, whereconst doc = ... is replaced by your own MathDocumentobject created by mathjax.document().

Once you have called MathJax.typesetClear(), you can remove the elements that you passed it or clear their contents and replace them with other content. Then callMathJax.typesetPromise() to typeset that new content.

If your input format is LaTeX, then the new content will have access to any macro definitions or labels that were defined in any previous content, and automatic equation numbering will continue with the next number, even if you remove equations with earlier numbers. In an editor setting, where the same content is typeset over and over, this can lead to errors about multiply-defined labels, incorrect application of macros before they are supposed to be defined, and equation numbers going up on each re-rendering.

To overcome these problems, you can use theMathJax.texReset() method to remove any previously-defined labels, and optionally set the automatic equation numbering starting value.

Note

The MathJax.texReset() command corresponds to

(...args) => { const jax = MathJax.startup.document.inputJax.tex; jax.reset(...args); }

Those not using MathJax Components can use these definitions, whereconst jax = ... is replaced by your TeX input jax instance.

To reset macro definitions, you can use thebegingroup extension to isolate the definitions used for one typesetting pass from the following ones. Its\begingroupSandbox is one way to do that, which you can process using

MathJax.tex2mml('\begingroupSandbox');

These techniques are illustrated in the example in the next section.

Editor Preview Example

The following code combines the mechanisms discussed above into an example that implements a basic editor with a preview that is updated on every change the the input area. This example uses the HTML that the user enters, updates an output area using that, and calls MathJax to process the expressions it contains. Of course, in practice, you would want to sanitize the user input to prevent the user from entering maliceous code, so this is just the bare-bones version meant to highlight how to handle the MathJax update portion of the erditor tasks.

The details are discussed after the code listing below.

1 2 3 4editor testing 5 21 22 23 24

Enter HTML with mathematics here:

25 26 27

Preview:

28
29 30 114 115 116

The style element at lines 5 through 20 just set the look and size of the input and output areas and header, which are found at lines 24 through 28. The script that begins at line 30 defines the code that runs the editor. It is placed within a function that is called immediately after it is defined in order to make the editor’s variables be local to the editor and not pollute the global namespace.

Lines 35 and 36 obtain the input and output elements for easy reference later.

Lines 38 and 39 define variables that are used to control when MathJax is called to do more typesetting. You don’t want to callMathJax.typesetPromise() on every keystroke, because if the user is typing quickly, that could cause several typesetting calls to be made while a typesetting action is already being performed, plus it is not a good idea to change the DOM that MathJax is actively updating while it is typesetting.

To avoid this, we use the variable mjRunning to specify when MathJax has been asked to typeset, but that typesetting hasn’t completed yet. We won’t queue any more typeset calls until the active one has finished. This is initially set to true so that no updates are performed until MathJax is loaded and ready (it is set tofalse later). The updatePending variable is used to specify that an update has been requested while MathJax is still typesetting, which means that when MathJax finishes typesetting, we should update again. That update may include several new characters that were entered while MathJax was typesetting, but we only update once, and we only change the DOM when MathJax is idle.

Lines 45 through 51 add an event listener to the textarea element that runs when its content changes (due to keystrokes, or copy and paste, etc.). If MathJax is currently running, we set theupdatePending value so that we don’t update now, but do so when MathJax is finished. Otherwise, we do the update immediately.

The updatePreview() function at lines 56 through 88 does the work of updating the preview area and typesetting its mathematics. It sets mjRunning to true so that if further changes occur to the input area, updates will be put off until after MathJax has typeset the current preview. We set the updatePending value to false since we are doing an update, and so we can tell if any updates are requested while MathJax is running.

Line 66 tells MathJax to forget about any mathematics that was previously typeset in the preview area. Without this, MathJax’s list of typeet math would grow with each character typed, as it holds onto all the previous copies of the math that it had typeset in the past. This call must come before the DOM is changed, so that MathJax can tell which math is inside the material being removed.

Line 71 tells MathJax to forget about any label commands that it has processed in the past and to reset automatic equation numbering to start at 1 again. Line 72 tells MathJax to throw away any macro definitions from the previous preview and start fresh. (This doesn’t remove any macros defined by auto-loaded extensions or ones loaded explicitly by require, however).

Line 76 replaces the preview output area with the content of the input area, and line 77 asks MathJax to typeset any mathematics it contains. This is a promise-based call, so the typesetting doesn’t start immediately, and the function returns right away. The .then()function is performed when the typesetting completes. It sets themjRunning variable to false and checks if any additional updates were requested while MathJax was typesetting, and if so, it does another updatePreview() call. If an error occurred during typesetting, line 87 traps that and reports the error.

The configuration at lines 93 through 111 tell MathJax to load thebegingroup TeX extension (needed for \begingroupSandbox), and adds that extension to the list of packages that TeX should use. It also adds single dollar signs as in-line math delimiters. Finally, it sets up a pageReady() function that does the normal page-ready actions, unsets the mjRunning variable, and then checks if the input area has any content (as it will in some browsers if the page is reloaded, for example), and if so, it updates the preview to show that initial content.

Line 114 loads MathJax into the page.