@@ -85,11 +85,27 @@ export interface ExplorerMathItem extends HTMLMATHITEM {
8585 */
8686 explorers : ExplorerPool ;
8787
88+ /**
89+ * Semantic id of the rerendered element that should regain the focus.
90+ */
91+ refocus : string ;
92+
8893 /**
8994 * @param {HTMLDocument } document The document where the Explorer is being added
9095 * @param {boolean } force True to force the explorer even if enableExplorer is false
9196 */
9297 explorable ( document : HTMLDOCUMENT , force ?: boolean ) : void ;
98+
99+ /**
100+ * @param {ExplorerMathDocument } document The explorer document being used
101+ * @returns {HTMLElement } The temporary focus element, if any
102+ */
103+ setTemporaryFocus ( document : ExplorerMathDocument ) : HTMLElement ;
104+
105+ /**
106+ * @param {HTMLElement } focus The temporary focus element, if any
107+ */
108+ clearTemporaryFocus ( focus : HTMLElement ) : void ;
93109}
94110
95111/**
@@ -143,9 +159,9 @@ export function ExplorerMathItemMixin<B extends Constructor<HTMLMATHITEM>>(
143159 public explorers : ExplorerPool ;
144160
145161 /**
146- * Semantic id of the rerendered element that should regain the focus.
162+ * @override
147163 */
148- protected refocus : string = null ;
164+ public refocus : string = null ;
149165
150166 /**
151167 * @override
@@ -192,21 +208,64 @@ export function ExplorerMathItemMixin<B extends Constructor<HTMLMATHITEM>>(
192208 this . state ( STATE . EXPLORER ) ;
193209 }
194210
211+ /**
212+ * @override
213+ */
214+ public state ( state : number = null , restore : boolean = false ) {
215+ if ( state < STATE . EXPLORER && this . explorers ) {
216+ for ( const explorer of Object . values ( this . explorers . explorers ) ) {
217+ if ( explorer . active ) {
218+ explorer . Stop ( ) ;
219+ }
220+ }
221+ }
222+ return super . state ( state , restore ) ;
223+ }
224+
195225 /**
196226 * @override
197227 */
198228 public rerender (
199229 document : ExplorerMathDocument ,
200230 start : number = STATE . RERENDER
201231 ) {
232+ const focus = this . setTemporaryFocus ( document ) ;
233+ super . rerender ( document , start ) ;
234+ this . clearTemporaryFocus ( focus ) ;
235+ }
236+
237+ /**
238+ * Focuses a temporary element during rerendering
239+ *
240+ * @param {ExplorerMathDocument } document The explorer document to use
241+ * @returns {HTMLElement } The temporary focus element, if any
242+ */
243+ public setTemporaryFocus ( document : ExplorerMathDocument ) : HTMLElement {
244+ let focus = null ;
202245 if ( this . explorers ) {
203246 const speech = this . explorers . speech ;
204- if ( speech && speech . attached ) {
247+ focus = speech ?. attached ? document . tmpFocus : null ;
248+ if ( focus ) {
205249 this . refocus = speech . semanticFocus ( ) ?? null ;
250+ const adaptor = document . adaptor ;
251+ adaptor . append ( adaptor . body ( ) , focus ) ;
206252 }
207253 this . explorers . reattach ( ) ;
254+ focus ?. focus ( ) ;
255+ }
256+ return focus ;
257+ }
258+
259+ /**
260+ * Removes the temporary element after rerendering
261+ *
262+ * @param {HTMLElement } focus The temporary focus element, if any
263+ */
264+ public clearTemporaryFocus ( focus : HTMLElement ) {
265+ if ( focus ) {
266+ const promise = this . outputData . speechPromise ?? Promise . resolve ( ) ;
267+ promise . then ( ( ) => setTimeout ( ( ) => focus . remove ( ) , 100 ) ) ;
208268 }
209- super . rerender ( document , start ) ;
210269 }
211270 } ;
212271}
@@ -220,11 +279,21 @@ export interface ExplorerMathDocument extends HTMLDOCUMENT {
220279 */
221280 infoIcon : HTMLElement ;
222281
282+ /**
283+ * An element ot use for temporary focus during rerendering
284+ */
285+ tmpFocus : HTMLElement ;
286+
223287 /**
224288 * The objects needed for the explorer
225289 */
226290 explorerRegions : RegionPool ;
227291
292+ /**
293+ * The MathItem with the active KeyExplorer, if any
294+ */
295+ activeItem : ExplorerMathItem ;
296+
228297 /**
229298 * Add the Explorer to the MathItems in the MathDocument
230299 *
@@ -401,11 +470,21 @@ export function ExplorerMathDocumentMixin<
401470 */
402471 public infoIcon : HTMLElement ;
403472
473+ /**
474+ * An element ot use for temporary focus during rerendering
475+ */
476+ public tmpFocus : HTMLElement ;
477+
404478 /**
405479 * The objects needed for the explorer
406480 */
407481 public explorerRegions : RegionPool = null ;
408482
483+ /**
484+ * The MathItem with the active KeyExplorer, if any
485+ */
486+ public activeItem : ExplorerMathItem = null ;
487+
409488 /**
410489 * Extend the MathItem class used for this MathDocument
411490 * and create the visitor and explorer objects needed for the explorer
@@ -425,8 +504,11 @@ export function ExplorerMathDocumentMixin<
425504 if ( ! options . a11y . speechRules ) {
426505 options . a11y . speechRules = `${ options . sre . domain } -${ options . sre . style } ` ;
427506 }
428- options . MathItem = ExplorerMathItemMixin ( options . MathItem , toMathML ) ;
429- options . MathItem . roleDescription = options . roleDescription ;
507+ const mathItem = ( options . MathItem = ExplorerMathItemMixin (
508+ options . MathItem ,
509+ toMathML
510+ ) ) ;
511+ mathItem . roleDescription = options . roleDescription ;
430512 this . explorerRegions = new RegionPool ( this ) ;
431513 if ( 'addStyles' in this ) {
432514 ( this as any ) . addStyles (
@@ -448,6 +530,22 @@ export function ExplorerMathDocumentMixin<
448530 SVGNS
449531 ) ,
450532 ] ) ;
533+ this . tmpFocus = this . adaptor . node ( 'mjx-focus' , {
534+ tabIndex : 0 ,
535+ style : {
536+ outline : 'none' ,
537+ display : 'block' ,
538+ position : 'absolute' ,
539+ top : 0 ,
540+ left : '-10px' ,
541+ width : '1px' ,
542+ height : '1px' ,
543+ overflow : 'hidden' ,
544+ } ,
545+ role : mathItem . ariaRole ,
546+ 'aria-label' : mathItem . none ,
547+ 'aria-roledescription' : mathItem . none ,
548+ } ) ;
451549 }
452550
453551 /**
@@ -467,6 +565,17 @@ export function ExplorerMathDocumentMixin<
467565 return this ;
468566 }
469567
568+ /**
569+ * @override
570+ */
571+ public rerender ( start ?: number ) {
572+ const active = this . activeItem ;
573+ const focus = active ?. setTemporaryFocus ( this ) ;
574+ super . rerender ( start ) ;
575+ active ?. clearTemporaryFocus ( focus ) ;
576+ return this ;
577+ }
578+
470579 /**
471580 * @override
472581 */
0 commit comments