Skip to content

Commit

Permalink
focusout works when focus changes in a blur call.
Browse files Browse the repository at this point in the history
  • Loading branch information
tugorez committed Sep 5, 2024
1 parent 4844cb4 commit e5c61e9
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 0 deletions.
4 changes: 4 additions & 0 deletions lib/web_ui/lib/src/engine/dom.dart
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,10 @@ extension DomHTMLDocumentExtension on DomHTMLDocument {
@JS('visibilityState')
external JSString get _visibilityState;
String get visibilityState => _visibilityState.toDart;

@JS('hasFocus')
external JSBoolean _hasFocus();
bool hasFocus() => _hasFocus().toDart;
}

@JS('document')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ final class ViewFocusBinding {
});

late final DomEventListener _handleFocusout = createDomEventListener((DomEvent event) {
// Tipically when the browser handles focus events, blur happens first and then focusout.
// When blur is not prevented, by the time focusout is called, the document.activeElement is set to <body />
// There are instances in the engine where in the middle of a blur event, focus is moved to another node.
if (domDocument.hasFocus() && domDocument.activeElement != domDocument.body) {
return;
}

event as DomFocusEvent;
_handleFocusChange(event.relatedTarget as DomElement?);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,31 @@ void testMain() {
expect(dispatchedViewFocusEvents[0].state, ui.ViewFocusState.focused);
expect(dispatchedViewFocusEvents[0].direction, ui.ViewFocusDirection.forward);
});

test('works even if focus is changed within a focus change hook', () {
final DomElement input1 = createDomElement('input');
final DomElement input2 = createDomElement('input');
final EngineFlutterView view = createAndRegisterView(dispatcher);
final DomEventListener focusInput1Listener = createDomEventListener((DomEvent event) {
input1.focusWithoutScroll();
});

view.dom.rootElement.append(input1);
view.dom.rootElement.append(input2);

input1.addEventListener('blur', focusInput1Listener);
input1.focusWithoutScroll();
// The event handler above should move the focus back to input1.
input2.focusWithoutScroll();
input1.removeEventListener('blur', focusInput1Listener);

expect(dispatchedViewFocusEvents, hasLength(1));

expect(dispatchedViewFocusEvents[0].viewId, view.viewId);
expect(dispatchedViewFocusEvents[0].state, ui.ViewFocusState.focused);
expect(dispatchedViewFocusEvents[0].direction, ui.ViewFocusDirection.forward);

});
});
}

Expand Down

0 comments on commit e5c61e9

Please sign in to comment.