@@ -202,6 +202,7 @@ export class _Mathfield implements Mathfield, KeyboardDelegateInterface {
202202 // the `change` event is dispatched
203203 private valueOnFocus : string ;
204204 private focusBlurInProgress = false ;
205+ private programmaticFocusInProgress = false ;
205206
206207 private geometryChangeTimer : ReturnType < typeof requestAnimationFrame > ;
207208
@@ -818,7 +819,10 @@ If you are using Vue, this may be because you are using the runtime-only build o
818819
819820 switch ( evt . type ) {
820821 case 'focus' :
821- this . onFocus ( { suppressEvents : true } ) ;
822+ // Skip handling DOM focus events that result from programmatic focus()
823+ // calls to prevent double onFocus() invocation (fixes #2685)
824+ if ( ! this . programmaticFocusInProgress )
825+ this . onFocus ( { suppressEvents : true } ) ;
822826 break ;
823827
824828 case 'blur' :
@@ -1349,6 +1353,7 @@ If you are using Vue, this may be because you are using the runtime-only build o
13491353 focus ( options ?: FocusOptions ) : void {
13501354 if ( this . disabled || this . focusBlurInProgress ) return ;
13511355 if ( ! this . hasFocus ( ) ) {
1356+ this . programmaticFocusInProgress = true ;
13521357 this . onFocus ( ) ;
13531358 this . model . announce ( 'line' ) ;
13541359 }
@@ -1754,6 +1759,11 @@ If you are using Vue, this may be because you are using the runtime-only build o
17541759 this . focusBlurInProgress = false ;
17551760 this . keyboardDelegate . focus ( ) ;
17561761 this . connectToVirtualKeyboard ( ) ;
1762+
1763+ // Clear the flag after the DOM focus event has been processed
1764+ setTimeout ( ( ) => {
1765+ this . programmaticFocusInProgress = false ;
1766+ } , 0 ) ;
17571767 }
17581768 } , 60 ) ;
17591769 }
0 commit comments