Skip to content

Rework the MathJax.typesetPromise usage.#1431

Open
drgrice1 wants to merge 3 commits into
openwebwork:PG-2.21from
drgrice1:mathjax-typeset-promise-rework
Open

Rework the MathJax.typesetPromise usage.#1431
drgrice1 wants to merge 3 commits into
openwebwork:PG-2.21from
drgrice1:mathjax-typeset-promise-rework

Conversation

@drgrice1
Copy link
Copy Markdown
Member

@drgrice1 drgrice1 commented Jun 7, 2026

The MathJax.typesetPromise calls no longer need to be passed through the MathJax.startupPromise according to @dpvc. See his comments on this in openwebwork/webwork2#2955.

Also, make the MathJax.typesetPromise calls to typeset popover contents specific to the particular popover that needs to be typeset instead of typesetting all popovers in the page. This is not done in the way that @dpvc suggested because moving the typesetting from the show.bs.popover event to the shown.bs.popover event results in visual motion as the popover is typeset and possibly resized, and that is not desirable. Instead a timeout is used. This works because Bootstrap creates the popover.tip element immediately after Bootstrap triggers the show.bs.popover event, and so when the timeout handler is executed it is available to work with. But that is still before the css transitions are complete which is when the shown.bs.popover event is triggered.

Note that in htdocs/js/Knowls/knowl.js and htdocs/js/DragNDrop/dragndrop.js, the typesetPromise method is often not yet defined on the MathJax object when those calls are made. That is okay, because if it isn't yet defined, that means that MathJax is still typesetting the page, and gets those elements. Generally the call is only needed for cases where those things are added to the page later.

Copy link
Copy Markdown
Contributor

@Alex-Jordan Alex-Jordan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not seeing any issues from using this branch. I'm not sure what to look at for testing. But I'll mark it as approved.

@dpvc
Copy link
Copy Markdown
Member

dpvc commented Jun 7, 2026

I still think that using the show.bs.popover to initiate the MathJax typesetting is the wrong place for this, and that you are working harder than necessary to overcome the fact that this is the wrong event to use for this. It relies on subtle timing issues: the fact that the popover isn't currently in the DOM, but MathJax's typesetting is in a promise, and so it not done until the next javascript idle time, with the expectation that the popover will be in place by the time MathJax runs. That seems fragile to me.

Instead, I'd suggest using the inserted.bs.popover event rather than show.bs.popover, since that seems to be exactly what you are looking for (the popover is in the DOM but not yet revealed). Then popover.tip will be available and you don't need all the fragile setTimeout() commands.

Then in feedback.js, you can combine the current show.bs.popover and shown.bs.popover to a single inserted.bs.popover event handler.

If you really want to avoid jitter from MathJax's typesetting of expressions, not just in popovers, but in the page itself, you could add

document.querySelector('#problem_body').style.visibility="hidden";

to the bottom of the mathjax-config.js file, and insert

      pageReady() {
        return MathJax.startup.defaultPageReady()
          .then(() => document.querySelector('#problem_body').style.visibility='');
      },

into the startup block of the configuration. This will hide the problem until after MathJax has typeset all the expressions.

drgrice1 added 2 commits June 7, 2026 05:56
The `MathJax.typesetPromise` calls no longer need to be passed through
the `MathJax.startupPromise` according to @dpvc. See his comments on
this in openwebwork/webwork2#2955.

Also, make the `MathJax.typesetPromise` calls to typeset popover
contents specific to the particular popover that needs to be typeset
instead of typesetting all popovers in the page. This is not done in the
way that @dpvc suggested because moving the typesetting from the
`show.bs.popover` event to the `shown.bs.popover` event results in
visual motion as the popover is typeset and possibly resized, and that
is not desirable. Instead a timeout is used.  This works because
Bootstrap creates the `popover.tip` element immediately after Bootstrap
triggers the `show.bs.popover` event, and so when the timeout handler is
executed it is available to work with. But that is still before the css
transitions are complete which is when the `shown.bs.popover` event is
triggered.

Note that in `htdocs/js/Knowls/knowl.js` and `htdocs/js/DragNDrop/dragndrop.js`,
the `typesetPromise` method is often not yet defined on the `MathJax`
object when those calls are made.  That is okay, because if it isn't yet
defined, that means that MathJax is still typesetting the page, and gets
those elements.  Generally the call is only needed for cases where those
things are added to the page later.
…event.

This is certainly better than a timeout, and I don't know how I missed
that event. To be honest I didn't know it existed and have missed it in
the documentation for years. Thanks @dpvc for pointing this out.
@drgrice1 drgrice1 force-pushed the mathjax-typeset-promise-rework branch from f2bae2e to 52c8539 Compare June 7, 2026 11:12
@drgrice1
Copy link
Copy Markdown
Member Author

drgrice1 commented Jun 7, 2026

I switched to the the inserted.bs.popover event. Thanks for pointing that out @dpvc. I didn't even know that event existed, and have missed that in the documentation for years. It certainly is the correct event, and is better than a timeout.

Copy link
Copy Markdown
Member

@dpvc dpvc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, with one question about an update that might not be needed.

Comment thread htdocs/js/Feedback/feedback.js Outdated
}
feedbackBtn.addEventListener('inserted.bs.popover', () => {
// Render MathJax previews.
if (window.MathJax) MathJax.typesetPromise([feedbackPopover.tip]).then(() => feedbackPopover.update());
Copy link
Copy Markdown
Member

@dpvc dpvc Jun 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't tested it, but I wonder if the popover update is still needed, as it hasn't been shown, yet, and showing it may get the position/size right automatically. Just a thought.

@dpvc
Copy link
Copy Markdown
Member

dpvc commented Jun 7, 2026

I didn't even know that event existed, and have missed that in the documentation for years.

I didn't know about it, either, but hoped there might be one for "created but not yet shown", and so looked for the list of events, and there it was!

@drgrice1
Copy link
Copy Markdown
Member Author

drgrice1 commented Jun 7, 2026

Yeah, I might have missed it partially due to the order they list the events in the documentation. The inserted event is listed before both the show and shown events. Yeah, I am just making excuses for not reading carefully, but it might have helped if they were listed in the order they are triggered.

@drgrice1
Copy link
Copy Markdown
Member Author

drgrice1 commented Jun 7, 2026

I will check on if the feedbackPopover.update is still needed or not.

@dpvc
Copy link
Copy Markdown
Member

dpvc commented Jun 7, 2026

it might have helped if they were listed in the order they are triggered.

Agreed. That would make things clearer.

I am seeing several instances where the function is not defined.  So
protect against calling it if it isn't.  Presumably the cases where it
is not defined will still be caught by the initial typeset.
@drgrice1
Copy link
Copy Markdown
Member Author

drgrice1 commented Jun 7, 2026

I added protection against calling the typesetPromise method when it is not defined in all cases. There were several instances where I was still seeing that it was not.

Also, it seems that the feedbackPopover.update() call is still needed. I still see cases where the popover does not get resized correctly if it is removed.

@dpvc
Copy link
Copy Markdown
Member

dpvc commented Jun 7, 2026

OK, sounds good. Thanks for checking!

drgrice1 added a commit to drgrice1/webwork2 that referenced this pull request Jun 7, 2026
This was suggested by @dpvc in
openwebwork/pg#1431.  The point is to prevent
the visual motion that occurs while the MathJax content is rendered.
This doesn't use the `#problem_body` id selector since that is not on
all problems (it isn't in Gateway tests), but uses the
`.problem-content` class selector instead, since that is on all
problems.  Also, a `for of` loop and `document.querySelectorAll` is
needed for tests, since there can be more than one problem.

I am not entirely sold on the empty area shown in the mean time, but it
does prevent the jitter for the text within the problem. Perhaps if
someone wants to toy with css, a loading block could be shown with a
transition that fades in the problem when the MathJax rendering is
completed.

Note that webwork2 renders MathJax in places outside of the problem
content, and those will not be affected by this.
Copy link
Copy Markdown
Contributor

@somiaj somiaj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A quick test of various rendering, popups, and everything worked. There are multiple approvals, but some changes since the last one, I'm going to merge soon so it can get more testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants