.. _startup-action:
#################################
Performing Actions During Startup
#################################
There are several ways to hook into the MathJax startup process so
that you can do additional configuration, perform actions after the
initial typesetting, and so on. The primary way to do this is to use
one of two hooks that can be set in the :js:data:`startup` block
of your configuration: the :func:`ready()` function and the
:func:`pageReady()` function.
The :func:`ready()` function is what MathJax calls when all the
requested MathJax components have been loaded, and MathJax is ready to
set up the internal objects needed to process the mathematics on the
page (like the input and output jax). This function builds those
structures, creates functions in the :js:data:`MathJax` object to make
typesetting and format conversion easy for you, sets up the
:js:meth:`pageReady()` call (described below), and creates a promise
for when that is complete. You can override the :js:meth:`ready()`
function with one of your own to replace that startup process
completely, or to perform actions before or after the usual
initialization. For example, you could do additional setup before
MathJax creates the objects it needs, or you could hook into the
typesetting promise to synchronize other actions with the completion
of the initial typesetting. Examples of these are given below.
The :js:meth:`pageReady()` function is performed when the HTML page is
loaded and ready to be typeset, and MathJax itself is ready (all its
components are loaded, and the internal objects have been created).
The default is for :js:meth:`pageReady()` to perform the initial
typesetting of the page, but you can override that to perform other
actions instead, such as delaying the initial typesetting while other
content is loaded dynamically, for example. The :js:meth:`ready()`
function sets up the call to :js:meth:`pageReady()` as part of its
default action.
The return value of the default :js:meth:`pageReady()` is a promise
that is resolved when the initial typesetting is finished. If you
override the :js:meth:`pageReady()` method, your function should
return a promise as well. For example, if your function calls
:meth:`MathJax.startup.defaultPageReady()`, then you should return the
promise that it returns (or a promise obtained from its
:js:meth:`then()` or :js:meth:`catch()` methods). If you don't, then
MathJax will think that the initial typesetting is complete even
though it isn't, which can lead to incorrect behavior if other
typesetting needs to be performed later.
Using these two functions separately or in combination gives you full
control over the actions that MathJax takes when it starts up, and
allows you to customize MathJax's startup process to suit your needs.
Several examples for common situations are given below.
-----
.. _initialization-actions:
Performing Actions During Initialization
========================================
If you want to perform actions after MathJax has loaded all the needed
components, you can set the :js:meth:`ready()` function to a function
that does the needed actions and calls
:meth:`MathJax.startup.defaultReady()` to perform the usual startup
process.
Actions coming before the :meth:`MathJax.startup.defaultReady()` call
are run before any initialization has been done. In particular, this
is before any input or output jax are created, so this is where
customization of the MathJax object definitions could be performed.
For example, you could modify the configuration blocks at this point,
or you could create subclasses of the MathJax objects that override
some of their methods to produce custom behavior, and then register
those subclasses with MathJax so they will be used in place of the
originals. It is also possible to create TeX extensions on the fly
and add them at this point.
Actions coming after the :meth:`MathJax.startup.defaultReady()` call
are run after initialization is complete. In particular, all the
internal objects used by MathJax (e.g., the input and output jax, the
math document, the DOM adaptor, etc) will have been created, and the
typesetting and conversion methods will have been created in the
:js:data:`MathJax` object.
In addition, the variable :js:data:`MathJax.startup.promise` will hold
a promise that is resolved when the initial typesetting is complete,
but note that the typesetting has not yet been performed at this
point. You can use this promise to set up actions that should occur
after the initial typesetting is complete. This is discussed further
in the next section.
.. code-block:: javascript
window.MathJax = {
startup: {
ready() {
console.log("MathJax is loaded, but not yet initialized");
MathJax.startup.defaultReady();
console.log("MathJax is initialized and the initial typeset is queued, but hasn't run");
MathJax.startup.promise.then(() => {
console.log("The initial typesetting is complete");
});
}
}
};
The console messages above indicate the MathJax's state at each point
in the code. For example, you can't do any typesetting in the section
before :js:meth:`MathJax.startup.defaultReady()`, while you
potentially could after it, thought it is better to wait for the
:js:data:`MathJax.startup.promise` before doing so, except in special
circumstances where you know that the typesetting will not cause any
extensions to be loaded dynamically.
.. _full-width-numbers:
Here is an example that uses the :js:meth:`ready()` function to
convert the numbers in the full-width Unicode block to their ASCII
counterparts for better formatting by MathJax.
.. code-block:: javascript
MathJax = {
startup: {
ready() {
MathJax.startup.defaultReady();
MathJax.startup.document.inputJax.tex.preFilters.add(
({math}) => {
math.math = math.math.replace(/[\uFF01-\uFF5E]/g,
(c) => String.fromCodePoint(c.codePointAt(0) - 0xFF00 + 0x20));
}
);
}
}
}
This configuration adds a pre-filter to the TeX input jax that
performs a substitution on the TeX source (``math.math``) for each
expression being processed that looks for full-width numerals and
replaces them with the corresponding ASCII numerals.
.. _print-all-math:
Here is an example that waits for the initial typesetting to complete
and then prints to the console the TeX code for all the expressions on
the page.
.. code-block:: javascript
MathJax = {
startup: {
ready() {
MathJax.startup.defaultReady();
MathJax.startup.promise.then(() => {
for (const item of MathJax.startup.document.math) {
console.log(item.math);
}
});
}
}
}
.. _asciimath-display-delimiters:
Finally, here is an example that modifies the AsciiMath input jax to
allow both in-line and display-mode equations by using ```...``` for
in-line delimiters and `````` ``...`` `````` for display-mode
delimiters.
.. code-block:: javascript
MathJax = {
loader: {load: ['input/asciimath', 'output/chtml']},
output: {font: 'mathjax-newcm'},
asciimath: {
delimiters: [['``','``'], ['`','`']]
},
startup: {
ready() {
const {AsciiMath} = MathJax._.input.asciimath_ts;
Object.assign(AsciiMath.prototype, {
_compile: AsciiMath.prototype.compile,
compile(math, document) {
math.display = (math.start?.delim === '``');
const result = this._compile(math, document);
const mstyle = result.childNodes[0].childNodes.pop();
mstyle.childNodes.forEach(child => result.appendChild(child));
if (math.display) {
result.attributes.set('display', 'block');
}
return result;
}
});
MathJax.startup.defaultReady();
}
}
};
This saves the old AsciiMath ``compile()`` function and replaces it
with a new one that removes the original ``mstyle`` element created by
AsciiMath that sets the display mode, and sets the mode on the outer
``math`` tag depending on the delimiter used.
-----
.. _post-typesetting:
Performing Actions After Typesetting
====================================
Often, you may need to wait for MathJax to finish typesetting the page
before you perform some action. To accomplish this, you can override
the :js:meth:`ready()` function, having it perform the
:js:meth:`MathJax.startup.defaultReady()` action, and then use the
:js:data:`MathJax.startup.promise` to queue your actions; these will be
performed after the initial typesetting is complete.
.. code-block:: javascript
window.MathJax = {
startup: {
ready: () => {
MathJax.startup.defaultReady();
MathJax.startup.promise.then(() => {
console.log('MathJax initial typesetting complete');
});
}
}
};
As an alternative, you can override the :js:meth:`pageReady()` function,
and use the promise returned from the
:meth:`MathJax.startup.defaultPageReady()` function:
.. code-block:: javascript
window.MathJax = {
startup: {
pageReady: () => {
return MathJax.startup.defaultPageReady().then(() => {
console.log('MathJax initial typesetting complete');
});
}
}
};
Be sure that you return the promise that you obtain from
:js:meth:`then()` method, otherwise :js:data:`MathJax.startup.promise`
will resolve before the initial typesetting (and your code) has been
performed, which may cause other code to run too soon.
Our first example above shows how to use
:js:data:`MathJax.startup.promise` within the :js:meth:`ready()`
function, but that promise is set up as soon as MathJax is loaded, so
it can be used outside of the :js:meth:`ready()` function. You must
be careful, however, that MathJax is loaded before you try to use it.
For example, if you use :attr:`defer` or :attr:`async` attributes on
the script tag that loads MathJax, then you need to be sure your code
that uses :js:meth:`MathJax.startup.promise` doesn't run until after
MathJax has been loaded.
One way to do that is to use :attr:`defer` on both the script that
loads MathJax and the one that uses
:js:data:`MathJax.startup.promise`, and to put your script **after**
the one that loads MathJax. Since deferred scripts run in the order
in which they appeared in the HTML document, that will guarantee that
:js:data:`MathJax.startup.promise` will be defined when you use it.
For example,
.. code-block:: html
where the :file:`mathjax-dependent-code.js` file contains the
:js:data:`MathJax.startup.promise` reference, such as
.. code-block:: javascript
MathJax.startup.promise.then(() => {
console.log('MathJax initial typesetting complete');
});
In this case, the MathJax-dependent code won't run until after MathJax
is loaded.
You can't use the :attr:`defer` attribute on a script tag without a
:attr:`src` attribute, but if you want to use an in-line script that
uses :js:data:`MathJax.startup.promise`, then you can use a script
with :attr:`type="module"`, as these have the :attr:`defer` attribute
by default. For example,
.. code-block:: html
will work to guarantee that the promise is defined when the script is
executed.
-----
.. _actions-summary:
Summary
=======
The following terms were discussed above:
.. js:method:: ready()
This is the function called when MathJax has loaded the
needed components and is ready to start setting up the
objects needed for typesetting the document. You can
override it in the ``startup`` section of the
:js:data:`MathJax` configuration object in order to
perform customization when MathJax loads, or to set up
actions to perform after the initial typesetting is
complete.
.. js:method:: MathJax.startup.defaultReady()
This is the default for the :js:meth:`ready()` function
above. You can call it from your ``ready()`` function
in order to perform the usual ``ready()`` action.
.. js:method:: pageReady()
This is the function called when the page is loaded and
MathJax is ready to perform typesetting. You can
override it in the ``startup`` section of the
:js:data:`MathJax` configuration object in order to do
your own processing, or to set up actions to perform
after the initial typesetting is complete.
:returns: A promise that resolves when the actions taken by your
function is complete.
.. js:method:: MathJax.startup.defaultPageReady()
This is the default for the :js:meth:`pageReady()` function
above. You can call it from your ``pageReady()`` function
in order to perform the usual ``pageReady()`` action.
:returns: A promise that resolves when the initial page typesetting is complete.
.. js:attribute:: MathJax.startup.promise
A promise that resolves when MathJax completes its
initially typesetting.
|-----|