Typesetting dynamic content

39 views
Skip to first unread message

Francois Chaplais

unread,
May 26, 2025, 3:24:02 AMMay 26
to MathJax Users
Here is how MathJax is loaded in my page:

<script type="text/javascript" id="MathJax-script" defer
                src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.js">
        </script>
                    <script>
                MathJax = {
                    loader: {load: ['ui/lazy']},
                    options: {
                        lazyMargin: '200px',
                        lazyAlwaysTypeset: null
                    },
                };
            </script>
-----------------
I have pointers/links to web content that may contain equations. When the link is clicked, the JS code that handles the event does this:
* It checks if the link points to an element in the DOM.

If the object is not in the DOM, it asks the server to fetch the element that is in the database. If so, the HTML code is returned, and the JS code  inserts somewhere and typesets it using typesetPromise, and the math is rendered.

If the object is in the DOM, it does the same thing without querying the server. The problem that I cannot presently solve is that the math is not rendered even though the typeset promise is used.

To make things clearer, in both cases, the math has not been typeset previously. In the first case, this is because the code comes from the server. In the second case, this is because the equations were out of view and had not been typeset due to lazy loading. As far as I can tell, the promise code is the same in both cases.

The only difference between the two cases is that, in the second case, the math code already exists somewhere else on the page and it has not been typeset yet. The math code copy is in a different element with a different ID, but until now I have failed to render the math. Can this be due to the fact that MJ has put it aside for later processing due to lazy load? 

When I call the typeset engine, the original math code is not in view, but the copy is. Can this confuse MathJax?

Help will be greatly appreciated.

Here is the incriminated code:
----------------------------------------------------------------------
let refContent = document.getElementById(ref);
            if (refContent == null) {
                axios.post(url, {
                    id: ref,
                    label: label,
                })
                    .then(function (response) {
                        let promise = Promise.resolve();
                        function typeset(code) {
                            promise = promise.then(() => MathJax.typesetPromise(code()))
                                .catch((err) => console.log('Typeset failed: ' + err.message));
                            return promise;
                        }

                        typeset(() => {
                            let htmlCode = response.data.html;
                            let randomString = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
                            let refHTML = '<div class="reference" data-controller="remove"  data-action="click->remove#remove" data-ref="' + ref + '" id="'+randomString+'">' + htmlCode + '</div>';
                            if (tag.slice(0, 1) === 'H') {
                                div.innerHTML = refHTML + div.innerHTML;
                            } else {
                                div.innerHTML += refHTML;
                            }
                            let element = document.getElementById(randomString);
                            if (element) {
                                LatexDOM.reveal(element);
                            }
                            return [div];
                        });
                    })
                    .catch(function (error) {
                        console.log(error);
                    })

            } else {
                let promise = Promise.resolve();
                function typeset(code) {
                    promise = promise.then(() => MathJax.typesetPromise(code()))
                        .catch((err) => console.log('Typeset failed: ' + err.message));
                    return promise;
                }
                typeset(() => {
                    let randomString = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
                    let refHTML = '<div class="reference" data-controller="remove"  data-action="click->remove#remove" data-ref="' + ref + '" id="'+randomString+'">';
                    let classes = refContent.classList;
                    if (classes.contains('section')) {
                        let tagOfRefContent = refContent.tagName.toLowerCase();
                        refHTML += '<' + tagOfRefContent + ' class="section" id="' + ref + '" data-controller="section" data-action="click->section#showHide">';
                        refHTML += refContent.innerHTML;
                        refHTML += '</' + tagOfRefContent + '>';
                        let refDiv = ref+'/div';
                        refHTML += '<div id="'+refDiv+'">';
                        let section = document.getElementById(refDiv);
                        if (section !== null) {
                            let sectionHTML = section.innerHTML;
                            refHTML += sectionHTML;
                        }
                        refHTML += '</div>';
                    } else {
                        refHTML += refContent.innerHTML;
                    }
                    refHTML += '</div>';
                    if (tag.slice(0, 1) === 'H') {
                        div.innerHTML = refHTML + div.innerHTML;
                    } else {
                        div.innerHTML += refHTML;
                    }
                    let element = document.getElementById(randomString);
                    if (element) {
                        LatexDOM.reveal(element);
                    }
                });
            }

Francois Chaplais

unread,
May 26, 2025, 3:29:15 AMMay 26
to MathJax Users
I forgot the 
return[div] 
at the end of the second case (it should have been there), but i have tried the "correct" code several times and it does not typeset the reference in the second case of the if-then-else.

Francois Chaplais

unread,
May 26, 2025, 4:06:37 AMMay 26
to MathJax Users
When I inspect the copied version in the dev tools, it has MathJax code that says:
<mjx-lazy data-mjx-lazy="6275" aria-hidden="true"></mjx-lazy>
So I guess that MJ has flagged this content as "typeset later".
Is there a way to unset this? Like, say to MathJax: lazy-6275 should be typeset now?
Or do I have to nuke the problem by reloading this original HTML with the LaTeX code inside?
François

Francois Chaplais

unread,
May 26, 2025, 5:27:14 AMMay 26
to MathJax Users
I have tried the nuke option, i.e., I fetch the HTML code from the server, and it kinda works.

Is there a way to disable the lazy load option at that stage?

François

Francois Chaplais

unread,
May 26, 2025, 5:37:48 AMMay 26
to MathJax Users
Actually, the nuke option only "kinda" works because, even though the code is not long, MJ still applies the lazy load options and only the math that is in view is rendered, and the rest does not typeset when I scroll to it.

Davide Cervone

unread,
May 26, 2025, 7:54:49 AMMay 26
to mathja...@googlegroups.com
MathJax does not know if the content is visible or not, and will try to typeset it anyway, so your in-DOM content will be processed by MathJax even through it is not visible.  Because you are moving that content by constructing a new serialized DOM string, and using that to set the innerHTML of another element, Your transferred DOM is not properly tied to the lazy typesetting internal state (and the IntersectionObserver that it uses to tell when the math comes into view).  That is why it doesn't get typeset.

I think the easiest solution would be to put the in-DOM content inside a container that tells MathJax to skip that content during typesetting, so that the in-DOM-but-hidden math is NOT typeset until you move it into the visible DOM.  For example, the default ignoreHtmlTags option uses mathjax_ignore as the class to ignore, so you could use

<div class="mathjax_ignore">
...
</div>

around the content that is hidden and will be moved into the visible DOM later.  That will prevent MathJax from processing it before it is moved.

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/5759aee8-a1d7-4a99-b9f2-0eb84fa6525cn%40googlegroups.com.

Francois Chaplais

unread,
Jun 3, 2025, 10:18:18 AMJun 3
to mathja...@googlegroups.com
a HUGE thanks for your answer, Davide. I was away from my computer for a few days, it's only now that I have been implementing your solution.

Basically, I add the MathJax excluding class to the whole document content and I build my intersection observers myself.

Technically, I generate the HTML from the PHP backend and I add some data attribute to all equations and inline/paragraph content. When the page is loaded, the data attributes are detected by stimulus https://stimulus.hotwired.dev/handbook/introduction and this creates an intersection observer for all of these elements. When the element is "view", I typeset it through a promise and dismiss the observer.

When some extra HTML content is added to a DOM element, thanks to your suggestion, it has not been processed by mathjax before, and things work fine because the stimulus controller that does the intersection observation for the element(s) detects its visibility and typesets the equation.

This is so cool!

Thanks again,
François



Davide Cervone

unread,
Jun 3, 2025, 10:58:51 AMJun 3
to mathja...@googlegroups.com
I'm glad you were able to get a solution working to your satisfaction.

Thank for letting us know.

Davide


Reply all
Reply to author
Forward
0 new messages