<script type="text/x-mathjax-config">
window.MathJax = {loader: {load: ['[tex]/cancel']},tex: {packages: {'[+]': ['cancel']}}};
</script>
<script> MathJax = {chtml: {displayAlign: 'left'}}; </script>
<script>
MathJax = {loader: {load: ['[tex]/cancel']},
tex: {packages: {'[+]': ['cancel']}},output: {displayAlign: 'left'},};</script>
--
You received this message because you are subscribed to the Google Groups "MathJax Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mathjax-user...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/mathjax-users/d67f5804-bb44-4135-bc24-274bb202f995n%40googlegroups.com.
I copied the window.MathJax etc. instruction from the MathJax documentation for MathJax 4:
Your answer makes it clear that for MathJax4 that instruction/syntax is incorrect.
It could be that the information on that documenation page is outdated.
As a novel user I did not have the knowledge which version of MathJax uses which syntax.
I intend to contact the JSXGraph team, and inform them of the desirability of moving to MathJax.typesetPromise()(Of course I will first search the JSXGraph issue tracker.)
(I have over the years submitted bug reports to the JSXGraph team; I have a cordial relation with the lead developer. )
On Sunday, November 2, 2025 at 5:33:07 PM UTC+1 Davide Cervone wrote:Cleon:There are several issues with the use of MathJax on the page you link to. First, you are loading MathJax v4, but are using what looks like a v2 configuration block:<script type="text/x-mathjax-config">window.MathJax = {loader: {load: ['[tex]/cancel']},tex: {packages: {'[+]': ['cancel']}}};</script>(but this would not actually work in v2, either, since the type="text/x-mathjax-config" would mean that it would not be executed until after MathJax is loaded, and then it would replace the MathJax object that has been set up (and includes all the MathJax functions) with this configuration object, rendering MathJax inoperable.For v3 and v4, you don't use type="text/x-mathjax-config" but instead use a plain <script> tag, like the one that follows the above tag:<script> MathJax = {chtml: {displayAlign: 'left'}}; </script>Note that this doesn't load or configure the cancel package. For v4, I would also use the generic output block rather than the chtml block, in case anyone switched the render to the SVG output. So you might use<script>MathJax = {loader: {load: ['[tex]/cancel']},tex: {packages: {'[+]': ['cancel']}},output: {displayAlign: 'left'},};</script>instead. Because the cancel package is autoloaded when it is first used, you don't technically have to load it explicitly (though doing so correctly probably would resolve your problem).I see that you are also leading jsxgraphcore.js, part of the JSXGraph package. It turns out that this package includes calls to MathJax to perform typesetting, and so there is a race condition between MathJax itself and JSXGRaph as to who does the typesetting first. The result depends on the state of the browser cache and the timing of network requests, which is why you may only see this problem sometimes. If MathJax does the typesetting, then it should work out, but if JSXGraph does, then that can lead to the problem you are seeing.When MathJax v3/v4 is available, jsxgraphcore.js calls MathJax.typeset() to perform the typesetting, but this is the non-promise-based typesetting call that doesn't handle dynamic loading of extensions, and instead throws a "retry" error that signals the caller that it needs to wait for the dynamic load to complete before trying again. The typeset() call in jsxgraphcore.js is enclosed in a try/catch structure that traps the retry error but doesn't properly respond to it, and instead issues a "MathJax (not yet) loaded" console error (I generally see 3 of these on your page). An error will occur in the try/catch block if MathJax isn't available yet, but also if it needs to load an extension, like the cancel package.To properly handle this, jsxgraphcore.js should be calling MathJax.typesetPromise() instead, and you may be able to resolve the issue by changing that call in the jsxgraphcore.js file. I didn't look further to see if jsxgraphcore.js uses the result of the typesetting, but if so, then this change would require more work, as other parts of the code would have to deal with promises that currently don't; that takes care and an understanding of their workflow that I don't have.Pre-loading the cancel extension should allow it to work, so give that a try.Davide
--
You received this message because you are subscribed to the Google Groups "MathJax Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mathjax-user...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/mathjax-users/25f9d0f3-40ee-4662-9b7d-a01d60854a78n%40googlegroups.com.
<script>// User sets global Option valueJXG.Options.text.useMathJax = true;window.addEventListener('load', () => {//// If not using MathJax, just do the user code//if (!JXG.Options.text.useMathJax) {userCode();return;}//// If user hasn't provided a config, set one up.//window.MathJax ??= {};//// MathJax already loaded, no need to wait.//if (MathJax.version) {console.log("[useMJ] MathJax already loaded");MathJax.startup.promise.then(userCode);return;}//// Cache the user's ready function, if any, and add our own.//window.MathJax.startup ??= {};const ready = MathJax.startup.ready;MathJax.startup.ready = () => {(ready || MathJax.startup.defaultReady)();console.log("MathJax setup.ready");MathJax.startup.promise.then(() => {console.log("MathJax startup.promise resolved");userCode();});}//// Load MathJax//const script = document.createElement('script');script.src = 'https://cdn.jsdelivr.net/npm/mathjax@4/tex-chtml.js';document.head.appendChild(script);console.log("MathJax script added");});function userCode() {const board = JXG.JSXGraph.initBoard('jxgbox', {boundingbox: [-0.5, 3.5, 5, -2],axis: true,grid: true,showNavigation: true,showCopyright: false});board.create('text',[0.1, 2, "\\begin{equation} \\begin{split} e^x & = 1 + x + \\frac{x^2}{2} + \\frac{x^3}{6} + \\cdots \\\\ & = \\sum_{n\\geq 0} \\frac{x^n}{n!} \\end{split} \\end{equation}"],{fontsize: 18,parse: false});board.create('text',[1, -1, "$y=x^2$"],{fontsize: 20,parse: false});console.log("Board and text defined");}</script>
window.addEventListener('load', () => {new Promise((resolve) => {//// If not using MathJax, just do the user code//if (!JXG.Options.text.useMathJax) {resolve();return;}//// If user hasn't provided a config, set one up.//window.MathJax ??= {};//// MathJax already loaded, no need to wait.//if (MathJax.version) {console.log("[useMJ] MathJax already loaded");MathJax.startup.promise.then(resolve);return;}//// Cache the user's ready function, if any, and add our own.//window.MathJax.startup ??= {};const ready = MathJax.startup.ready;MathJax.startup.ready = () => {(ready || MathJax.startup.defaultReady)();console.log("MathJax setup.ready");MathJax.startup.promise.then(() => {console.log("MathJax startup.promise resolved");resolve();});}//// Load MathJax//const script = document.createElement('script');script.src = 'https://cdn.jsdelivr.net/npm/mathjax@4/tex-chtml.js';document.head.appendChild(script);console.log("MathJax script added");}).then(userCode);});
<script src="https://cdn.jsdelivr.net/npm/jsxgraph/distrib/jsxgraphcore.js"></script><script>(() => {let board = JXG.JSXGraph.initBoard('jxgbox', {boundingbox: [-0.5, 3.5, 5, -2],axis: true,grid: true,showNavigation: true,showCopyright: false});let mj_txt = board.create('text',[0.1, 2, "\\begin{equation} \\begin{split} e^x & = 1 + x + \\frac{x^2}{2} + \\frac{x^3}{6} + \\cdots \\\\ & = \\sum_{n\\geq 0} \\frac{x^n}{n!} \\end{split} \\end{equation}"],{fontsize: 18,parse: false});let mj_txt_2 = board.create('text',[1, -1, "$y=x^2$"],{fontsize: 20,parse: false});console.log("Board and text defined");})();</script><script>// User sets global Option valueJXG.Options.text.useMathJax = true;window.addEventListener('load', () => {//// If not using MathJax, we're done//if (!JXG.Options.text.useMathJax) return;//// Load MathJax if it isn't already configured or loaded.// (This assumes that if there is a MathJax configuration,// the page will also load MathJax itself.)//if (!window.MathJax) {const script = document.createElement('script');script.src = 'https://cdn.jsdelivr.net/npm/mathjax@4/tex-chtml.js';document.head.appendChild(script);console.log("loading MathJax");}});</script>
To view this discussion visit https://groups.google.com/d/msgid/mathjax-users/7faa179e-2864-4687-b8fe-f025530a1e93n%40googlegroups.com.
<script>
const board = JXG.JSXGraph.initBoard('jxgbox', {boundingbox: [-0.5, 3.5, 5, -2],axis: true,grid: true,showNavigation: true,showCopyright: false});board.create('text',[0.1, 2, "\\begin{equation} \\begin{split} e^x & = 1 + x + \\frac{x^2}{2} + \\frac{x^3}{6} + \\cdots \\\\ & = \\sum_{n\\geq 0} \\frac{x^n}{n!} \\end{split} \\end{equation}"],{fontsize: 18,parse: false});board.create('text',[1, -1, "$y=x^2$"],{fontsize: 20,parse: false});
// User sets global Option valueJXG.Options.text.useMathJax = true;window.addEventListener('load', () => {//
// If using MathJax and it is not already loaded, load it.//if (JXG.Options.text.useMathJax && !window.MathJax?.version) {
const script = document.createElement('script');script.src = 'https://cdn.jsdelivr.net/npm/mathjax@4/tex-chtml.js';document.head.appendChild(script);}
});</script>
<script>
const board = JXG.JSXGraph.initBoard('jxgbox', {boundingbox: [-0.5, 3.5, 5, -2],axis: true,grid: true,showNavigation: true,showCopyright: false});board.create('text',[0.1, 2, "\\begin{equation} \\begin{split} e^x & = 1 + x + \\frac{x^2}{2} + \\frac{x^3}{6} + \\cdots \\\\ & = \\sum_{n\\geq 0} \\frac{x^n}{n!} \\end{split} \\end{equation}"],{fontsize: 18,parse: false});board.create('text',[1, -1, "$y=x^2$"],{fontsize: 20,parse: false});
// User sets global Option valueJXG.Options.text.useMathJax = true;window.addEventListener('load', () => {
new Promise((resolve, reject) => {//// If not using MathJax or it is already loaded,// resolve the promise,// otherwise// load MathJax.//if (!JXG.Options.text.useMathJax || window.MathJax?.version) {resolve();} else {
const script = document.createElement('script');script.src = 'https://cdn.jsdelivr.net/npm/mathjax@4/tex-chtml.js';
script.onerror = reject;script.onload = () => MathJax.startup.promise.then(resolve);document.head.appendChild(script);}}).then(() => {console.log("MathJax ready"); // do your MathJax-based stuff here}).catch((err) => {console.error("Can't load MathJax");});});</script>
To view this discussion visit https://groups.google.com/d/msgid/mathjax-users/1f8b4bac-cf69-4c03-b82f-fa0da27c27fdn%40googlegroups.com.
- Caters for a user config, or not (I found I had to define MathJax.startup if not already, or things just came to a halt)
- Caters for what JSXGraph would do internally regarding typesetting only those elements that should be typeset.
The output is as desired for each of the on/off scenarios described. (I think JSXGraph is actually directing the individual element typeset cases, but I think what I have is correct.)
I commented out this line from your solution given my reorganising. It should be removed, right?script.onload = () => MathJax.startup.promise.then(resolve);
I tried to extract a more meaningful error message (it also threw that error if there were coding errors, not only MathJax loading errors), but couldn't.
// Array to hold single text elements for processing, if useMathJax: trueconst procWithMjArr = [];
new Promise((resolve, reject) => {//
// Add text nodes that need MathJax to the procWithMjArr array// and hide any that don't from MathJax.//txtArr.forEach( (txt) => {console.log(txt.id, "useMathJax ?", txt.visProp.usemathjax);if (txt.visProp.usemathjax) {procWithMjArr.push(txt.baseElement.rendNode);} else {txt.baseElement.rendNode.classList.add('mathjax_ignore');}});console.log("procWithMjArr", procWithMjArr)if (JXG.Options.text.useMathJax || procWithMjArr.length > 0) {console.log("MathJax needed because");if (JXG.Options.text.useMathJax) {console.log(" MathJax global option is true");}if (procWithMjArr.length > 0) {console.log(" 1 or more text eles need processing");}if (window.MathJax?.version) {console.log("MathJax already loaded, so wait for it");MathJax.startup.promise.then(resolve);} else {console.log("Loading MathJax");
const script = document.createElement('script');script.src = 'https://cdn.jsdelivr.net/npm/mathjax@4/tex-chtml.js';script.onerror = reject;
script.onload = () => {console.log("MathJax loded");MathJax.startup.promise.then(resolve)};document.head.appendChild(script);}} else {console.log("MathJax not needed");resolve();}}).catch((err) => {throw Error("Can't load or run MathJax.");}).then(() => {if (window.MathJax?.version) {console.log("MathJax ready, then...");//
// do your MathJax-based stuff here
//return MathJax.typesetPromise(procWithMjArr);}}).catch((err) => console.log(err)).then(() => console.log('done'));
To view this discussion visit https://groups.google.com/d/msgid/mathjax-users/1d7eecd4-d50f-4a31-81c4-b5c838f40cc6n%40googlegroups.com.
(1) On your first point, I was using Chrome on Windows to develop version 2. If I didn't have MathJax.startup ??= {}; the script would just stop. Any console log after that would not appear and MathJax wouldn't load. no errors, it just stopped.
Now in version 2 (after probably changing several things before posting it) on each of Chrome, Firefox, Edge, I have a new experience, whereby MathJax doesn't load, and I get the error "Error: Can't find or load MathJax, or some other unspecified error."
(BTW here is a possible bug report. When I used Edge just now, on first load it complained about not finding speech-worker.js, then on a hard refresh, I got:(speech-worker.js)Uncaught (in promise) TypeError: Cannot convert undefined or null to object (speech-worker.js)I keep getting this error on each soft or hard refresh. But not so on other browsers. When I revisited later, it didn't give the error.)
(2) Regarding your item 2 (with global set to false), that was working as desired for me in my v2, because I had set MathJax.startup.typeset = false; and then only typeset the 2nd expression (or whatever text element was actually set to useMathJax = true) at the end.
So I reinstated these lines (into your latest iteration) because if they're not there, the expected "global false" behaviour is not correct (that is, everything gets typeset, as you experienced).
//
// If user hasn't provided a config, set one up.
//
window.MathJax ??= {};
// If user hasn't defined startup
MathJax.startup ??= {};
// Pause typesetting
MathJax.startup.typeset = false;I found the 3rd line needs both the first and second if the user hasn't provided a config. Otherwise it throws "Error: Can't find or load MathJax."
(4) Thank you for sorting the .then() and .catch() order.However, any coding errors in the
- User's construction appropriately get caught by that last catch; but
- Any errors in the JSXGraph internals portion were giving the "Error: Can't find or load MathJax." error from the first catch(). I changed it to be the same as the final catch() and now it gives meaningful errors.
(5) So I think (hope) we're done.
--
You received this message because you are subscribed to the Google Groups "MathJax Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mathjax-user...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/mathjax-users/1435c862-5fb2-40ed-a2ad-24781e5f8c0an%40googlegroups.com.
MathJax = {options: {compileError(document, math, error) {alert(`Error: "${error.message}" in`, '\n', math.math);document.compileError(math, error);}}}
To view this discussion visit https://groups.google.com/d/msgid/mathjax-users/a2ec7357-4166-49f6-bd47-d893c09a1b41n%40googlegroups.com.
I now only see those errors intermittently, and haven't concluded anything about them yet.
One thing I have noticed is the \cancel script doesn't load (in time?) reasonably often.
So here's a version that's aimed at a newbie JSXGraph user who doesn't want to fiddle with any options - just enter the math in the text field and have JSXGraph handle the rest.
b. It requires double backslashes on the math (which trips up a lot of users) (There should be a way around this...?)
c. Back ticks are used for template literals of course, and some people use them for quotation marks, so this approach could cause problems for them.
(1) Have I set up the loader properly?
(2) Anything I've missed?
txtArr.push(board.create('text',[0.1, 5.5, String.raw`Uses <code>\begin{...}</code> so MathJax: \begin{equation} \begin{split} e^x & = 1 + x + \frac{x^2}{2} + \frac{x^3}{6} + \cdots \\ & = \sum_{n\geq 0} \frac{x^n}{n!} \end{split} \end{equation}`],{fontsize: 17}));
const raw = String.raw;
txtArr.push(board.create('text',[0.1, 5.5, raw`Uses <code>\begin{...}</code> so MathJax: \begin{equation} \begin{split} e^x & = 1 + x + \frac{x^2}{2} + \frac{x^3}{6} + \cdots \\ & = \sum_{n\geq 0} \frac{x^n}{n!} \end{split} \end{equation}`],{fontsize: 17}));
tex = {$: (strings, ...values) => '\\(' + String.raw(strings, ...values) + '\\)',$$: (strings, ...values) => '\\[' + String.raw(strings, ...values) + '\\]',};
txtArr.push(board.create('text',[0.1, 5.5,`Uses in-line math ${tex.$`\frac{\sqrt{1-x^2}}{1-x}`} in a sentence.`],{fontsize: 17}));
txtArr.push(board.create('text',[0.1, 5.5,'Uses in-line math ' + tex.$`\frac{\sqrt{1-x^2}}{1-x}` + ' in a sentence.'],{fontsize: 17}));
To view this discussion visit https://groups.google.com/d/msgid/mathjax-users/c7bb7801-d388-4845-9f70-fc58ba0dc481n%40googlegroups.com.
(1) I haven't had a chance to test things much since I've had to go on to other things. The loading errors from memory were mostly when I went to other browsers to check things were OK, and I suspect some of the issues were due to the \cancel package not being in cache.
There's an example here that uses a template literal, to convince myself it could work in amongst the other back ticks. It's messy (for a newbie) but it works.
To view this discussion visit https://groups.google.com/d/msgid/mathjax-users/db5b6f30-add0-417a-8b3f-ea25098ea141n%40googlegroups.com.