diff --git a/js/accessibility/pdom/PDOMTree.js b/js/accessibility/pdom/PDOMTree.js index d597f9f7a..c34460bc4 100644 --- a/js/accessibility/pdom/PDOMTree.js +++ b/js/accessibility/pdom/PDOMTree.js @@ -402,7 +402,11 @@ const PDOMTree = { * @private */ afterOp( focusedNode ) { - focusedNode && focusedNode.focusable && focusedNode.focus(); + + // If Scenery is in the middle of dispatching focus events, it is buggy to change focus again internally. + if ( !BrowserEvents.dispatchingFocusEvents ) { + focusedNode && focusedNode.focusable && focusedNode.focus(); + } BrowserEvents.blockFocusCallbacks = false; }, diff --git a/js/input/BrowserEvents.js b/js/input/BrowserEvents.js index 95e088b8a..45821a1bd 100644 --- a/js/input/BrowserEvents.js +++ b/js/input/BrowserEvents.js @@ -23,6 +23,10 @@ const BrowserEvents = { // caused by user interaction. blockFocusCallbacks: false, + // True while Scenery is dispatching focus and blur related events. Scenery (PDOMTree) needs to restore focus + // after operations, but that can be very buggy while focus events are already being handled. + dispatchingFocusEvents: false, + /** * Adds a Display to the list of displays that will be notified of input events. * @public @@ -763,12 +767,16 @@ const BrowserEvents = { // NOTE: Will be called without a proper 'this' reference. Do NOT rely on it here. + BrowserEvents.dispatchingFocusEvents = true; + // Update state related to focus immediately and allowing for reentrancy for focus state // that must match the browser's focus state. FocusManager.updatePDOMFocusFromEvent( BrowserEvents.attachedDisplays, domEvent, true ); BrowserEvents.batchWindowEvent( new EventContext( domEvent ), BatchedDOMEventType.ALT_TYPE, 'focusIn', true ); + BrowserEvents.dispatchingFocusEvents = false; + sceneryLog && sceneryLog.OnInput && sceneryLog.pop(); }, @@ -778,12 +786,15 @@ const BrowserEvents = { // NOTE: Will be called without a proper 'this' reference. Do NOT rely on it here. + BrowserEvents.dispatchingFocusEvents = true; + // Update state related to focus immediately and allowing for reentrancy for focus state - // that must match the browser's focus state. FocusManager.updatePDOMFocusFromEvent( BrowserEvents.attachedDisplays, domEvent, false ); BrowserEvents.batchWindowEvent( new EventContext( domEvent ), BatchedDOMEventType.ALT_TYPE, 'focusOut', true ); + BrowserEvents.dispatchingFocusEvents = false; + sceneryLog && sceneryLog.OnInput && sceneryLog.pop(); },