-
Notifications
You must be signed in to change notification settings - Fork 237
Handle clicking and tabbing to focusable HTML elements within expressions. #1421
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #1421 +/- ##
========================================
Coverage 86.51% 86.51%
========================================
Files 340 340
Lines 85993 85993
Branches 4825 4825
========================================
Hits 74397 74397
Misses 11596 11596 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
zorkow
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think in addHtmlEvents it is necessary to move from a () =>{} syntax to functiuon() {} syntax, as the arrow function should automatically bind the correct this context.
Or is there a particular reason that I miss?
| protected addHtmlEvents() { | ||
| for (const html of Array.from(this.node.querySelectorAll('mjx-html'))) { | ||
| const stop = (event: Event) => { | ||
| const stop = function (event: Event) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you changing this?
| } | ||
| } | ||
| }; | ||
| }.bind(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arrow functions should automatically bind to the correct this context. So I don't think this is necessary.
This PR improvers support for interactive elements within mathematical expressions, such as input areas and checkboxes. In previous versions, these could not be focused by clicking (as the clicks were processed by the explorer itself). These clicks are now allowed to be processed by the input elements. Further, the processing of tab keypresses is extended to properly tab among the focusable elements within the expression, so forward and back tabbing will cycle through the focusable elements as expected.
When exploring an expression, if the currently selected node has focusable children, then pressing tab or enter will focus that child node. If a focusable element is focused, exploration is suspended until the escape key is pressed, in which case the closes parent MathML node with speech is selected. That way, you can move in and out of the focusable items easily while exploring an expression.
Also, clicks and double clicks within the expression are no longer prevented from propagating, so that if an expression is within a button or label element, for example, the clicks on the math will still reach the containing element.
This resolves several issues: mathjax/MathJax#3501, mathjax/MathJax#3491.
Details in the code
The new
tabsproperty holds the focusable elements that are in the expression. This is used to find the next/previous item to focus when tab is pressed. This used to be done using theanchorsproperty, buttabsincludes all focusable elements, not just the anchors. Theanchorsproperty is still needed, however, because the anchors are taken out of the output and handled separately so as not to focus an element that is inside a container that hasaria-hiddenset. (The other focusable items are still in anaria-hiddencontainer, so that is technically not legal, so will need to be addressed in the future.)The change to the
FocusIn()method is to prevent the explorer from getting changing the focused item when the focused item is within an HTML fragment inside the expression.The change at line 648 is to prevent the explorer from changing the focus when an internal HTML element is clicked (the
this.clickedproperty will be set by theMouseDown()function in that case).The
escapeKey()function now checks if the current target is within embedded HTML, and moves to the closest parent speech node in that case.The
tabKey()function now has to take thetabsarray into account. the newactivevariable is either the currently selected speech node, or the internal HTML focused element. This is used to find the next focusable element. The comments in the code should tell you what is happening. Most of the changes are to move totabsfromanchors, and to move some of the details to separate methods (liketabTo()).The case of shift-tabbing when the top-level speech node is selected is a special case because we want to let the browser process the tab to get the previous focusable item, but since the speech node currently has the focus, and it is at the end of the expression we need to prevent tabbing to one of the internal inputs. The
tabOut()method is used to handle this. It does so by temporarily disabling the internal HTML nodes by making themdisplay: noneso the browser will skip them when back tabbing, and then making them visible again after the tab has been processed. This can cause visual jitter, but was the only way I could find to get it to work.The
tabTo()method either selects the given node (if it is a link), or focuses the node (if it is internal HTML).The
enterKey()method is modified (818-822) to check for internal focusable items, and focuses the first one, if any.The
getTabs()andgetInternaltabs()methods look up any focusable elements within a given node.Finally, the
stopfunction inaddHtmlEvents()only stops propagation for keyboard events (this is what allows other events to propagate to the expression's containers). For mouse events, it setsthis.clickedso thatFocusIn()will not take over the focus. Themousedownevent handler is added to support that.