Skip to content

Commit e61d152

Browse files
authored
fix(material/autocomplete): incorrectly resolving focused element in shadow DOM (angular#30619)
There are a few places where the autocomplete was checking if it has focus which were breaking down when inside the shadow DOM, because `document.activeElement` will point to the closest shadow root. These changes consolidate the places where we check for focus and use our utility for resolving the focused element through shadow boundaries. Fixes angular#30611.
1 parent 810495c commit e61d152

File tree

1 file changed

+9
-7
lines changed

1 file changed

+9
-7
lines changed

src/material/autocomplete/autocomplete-trigger.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,9 @@ import {
1919
PositionStrategy,
2020
ScrollStrategy,
2121
} from '@angular/cdk/overlay';
22-
import {_getEventTarget} from '@angular/cdk/platform';
22+
import {_getEventTarget, _getFocusedElementPierceShadowDom} from '@angular/cdk/platform';
2323
import {TemplatePortal} from '@angular/cdk/portal';
2424
import {ViewportRuler} from '@angular/cdk/scrolling';
25-
import {DOCUMENT} from '@angular/common';
2625
import {
2726
AfterViewInit,
2827
ChangeDetectorRef,
@@ -147,7 +146,6 @@ export class MatAutocompleteTrigger
147146
private _changeDetectorRef = inject(ChangeDetectorRef);
148147
private _dir = inject(Directionality, {optional: true});
149148
private _formField = inject<MatFormField | null>(MAT_FORM_FIELD, {optional: true, host: true});
150-
private _document = inject(DOCUMENT);
151149
private _viewportRuler = inject(ViewportRuler);
152150
private _scrollStrategy = inject(MAT_AUTOCOMPLETE_SCROLL_STRATEGY);
153151
private _renderer = inject(Renderer2);
@@ -216,8 +214,7 @@ export class MatAutocompleteTrigger
216214
// If the user blurred the window while the autocomplete is focused, it means that it'll be
217215
// refocused when they come back. In this case we want to skip the first focus event, if the
218216
// pane was closed, in order to avoid reopening it unintentionally.
219-
this._canOpenOnNextFocus =
220-
this._document.activeElement !== this._element.nativeElement || this.panelOpen;
217+
this._canOpenOnNextFocus = this.panelOpen || !this._hasFocus();
221218
};
222219

223220
/** `View -> model callback called when value changes` */
@@ -424,7 +421,7 @@ export class MatAutocompleteTrigger
424421
// true. Its main purpose is to handle the case where the input is focused from an
425422
// outside click which propagates up to the `body` listener within the same sequence
426423
// and causes the panel to close immediately (see #3106).
427-
this._document.activeElement !== this._element.nativeElement &&
424+
!this._hasFocus() &&
428425
(!formField || !formField.contains(clickTarget)) &&
429426
(!customOrigin || !customOrigin.contains(clickTarget)) &&
430427
!!this._overlayRef &&
@@ -551,7 +548,7 @@ export class MatAutocompleteTrigger
551548
}
552549
}
553550

554-
if (this._canOpen() && this._document.activeElement === event.target) {
551+
if (this._canOpen() && this._hasFocus()) {
555552
// When the `input` event fires, the input's value will have already changed. This means
556553
// that if we take the `this._element.nativeElement.value` directly, it'll be one keystroke
557554
// behind. This can be a problem when the user selects a value, changes a character while
@@ -580,6 +577,11 @@ export class MatAutocompleteTrigger
580577
}
581578
}
582579

580+
/** Whether the input currently has focus. */
581+
private _hasFocus(): boolean {
582+
return _getFocusedElementPierceShadowDom() === this._element.nativeElement;
583+
}
584+
583585
/**
584586
* In "auto" mode, the label will animate down as soon as focus is lost.
585587
* This causes the value to jump when selecting an option with the mouse.

0 commit comments

Comments
 (0)