MathJax 4: startup.promise resolves before DOM has time to update
Issue Summary
Using startup.promise.then() to do post-typesetting adjustments to DOM elements, we mentioned that in MathJax 4 our document.querySelector()s of mjx elements retrieve no elements. Sometimes though, after several random reloads trying to find our error, we actually do get them. This lead us to believe that the promise resolves before the elements are added to the DOM or rather that the promise resolves before the DOM has had time to mention the update, as turning our post-typeset callbacks into renderActions actually tells us that the elements we are interested in are indeed part of the DOM. It feels like the querySelectors should be able to find the items, but they do not. It looks like the promise resolving our callback happens before the actual typeset is complete.
Steps to Reproduce:
- Add a callback to typeset.promise via then()
- Call something akin to
document.querySelectorAll('mjx-container svg g[data-mml-node="mtable"]:first-of-type > g[data-mml-node="mtr"]') - Get no results. Sometimes.
Technical details:
- MathJax Version: 4.0
- Client OS: Any (tested on Windows and Mac)
- Browser: Chrome, Firefox version 119
I am using a MathJax configuration that is irrelevant to the issue.
and loading MathJax via
<script src="/self/hosted/mathjax4/tex-svg.js"></script>
Supporting information:
Here you have a minimal page you can test the issue with:
<!DOCTYPE html>
<html>
<head>
<title>Math Test</title>
<script>
function turnRed() {
console.log("Turn Red"); // called from startup.promise ... nothing happens
for(const row of document.querySelectorAll('g[data-mml-node="mtable"] > g[data-mml-node="mtr"]')) {
row.style.color = "red";
}
}
window.MathJax = {
startup: {
ready: () => {
window.MathJax.startup.defaultReady();
window.MathJax.startup.promise.then(() => {
turnRed();
});
}
}
}
</script>
<script defer type="text/javascript" id="MathJax-script" defer src="/support/vendor/mathjax/tex-svg.js"></script>
</head>
<body>
<h1>MathJax Testpage</h1>
<button onclick="turnRed()">Turn Red</button>
<span>$$\begin{eqnarray*}
a &=& b \\
a^2 &=& ab \\
2a^2 &=& a^2 + ab \\
2a^2-2ab &=& a^2 - ab \\
2a(a-b) &=& a (a-b) \\
2a &=& a \\
2 &=& 1
\end{eqnarray*}$$</span>
</body>
</html>
What I expect to happen?
turnRed gets called after typesetting and turn each row of the equation red
What happens?
Nothing happens after typesetting. Pressing the "Turn Red" button after the page loaded and a bit of time passed does work though. Changing the callback from turnRed(); to setTimeout(turnRed, 1); also works.
When I substitute https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js for the source for MathJax in your example document, it works as expected for me (the math turns red without any other action.) This is in Firefox, Chrome, and Safari on MacOS.
Can you try that URL to see if that works for you?
Yes it works with Mathjax 3. It does not work in Mathjax 4.
Try with https://cdn.jsdelivr.net/npm/[email protected]/tex-svg.js. It does not work.
OOPS, sorry. Working on answering too many different issues!
It turns out that this is the same issue as #3130: the MathJax.startup.defaultPageReady() function mishandles one of the promises, and that leads to the MathJax.startup.promise being resolved too early. One solution would be to use
window.MathJax = {
startup: {
ready: () => {
window.MathJax.startup.defaultReady();
window.MathJax.startup.promise.then(() => {
turnRed();
});
},
pageReady() {
const CONFIG = MathJax.config.startup;
const output = MathJax.config.output;
return (CONFIG.loadAllFontFiles && output.font ? output.font.loadDynamicFiles() : Promise.resolve())
.then(CONFIG.typeset && MathJax.typesetPromise ?
() => MathJax.startup.typesetPromise(CONFIG.elements) : Promise.resolve());
}
}
};
as the configuration (this in-lines the correct `defaultPageReady()` function). I've already made a PR for the correction that resolve this.
Thanks for the update. I guess we will just wait until you fixed this in an actual release instead of working around it ourselves just to remove the workaround at the next best moment.
OK, very good. We should have beta.5 soon and hope to have the official 4.0 release by the end of the year.
Is there any update on the roadmap? We are inclined to go with the workaround and stick with beta.4. Our goal is to decide upon a mathjax version before the end of the month.
As you can see, the release didn't happen as we had hoped. The updates to the expression explorer (that would lead to the beta.5 release) have taken longer than expected, and have push everything back. It is likely to still be several months before the official 4.0.