Skip to content

Commit 11c48cc

Browse files
committed
fix(cdk/menu): avoid resetting the scroll position when using the mouse
The CDK menu has some logic that forwards focus to the first item when the host is focused. The problem is that every time the user clicks on the scrollbar, they blur the current item and focus the menu which then forwards focus back to the first item which in turn causes the scroll position to jump to the top. These changes add some logic to not forward focus when focus comes from a mouse interaction. Fixes #30130.
1 parent 2219b11 commit 11c48cc

File tree

1 file changed

+18
-2
lines changed

1 file changed

+18
-2
lines changed

src/cdk/menu/menu-base.ts

+18-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import {_IdGenerator, FocusKeyManager, FocusOrigin} from '@angular/cdk/a11y';
9+
import {_IdGenerator, FocusKeyManager, FocusMonitor, FocusOrigin} from '@angular/cdk/a11y';
1010
import {Directionality} from '@angular/cdk/bidi';
1111
import {
1212
AfterContentInit,
@@ -43,7 +43,6 @@ import {PointerFocusTracker} from './pointer-focus-tracker';
4343
'[id]': 'id',
4444
'[attr.aria-orientation]': 'orientation',
4545
'[attr.data-cdk-menu-stack-id]': 'menuStack.id',
46-
'(focus)': 'focusFirstItem()',
4746
'(focusin)': 'menuStack.setHasFocus(true)',
4847
'(focusout)': 'menuStack.setHasFocus(false)',
4948
},
@@ -52,6 +51,7 @@ export abstract class CdkMenuBase
5251
extends CdkMenuGroup
5352
implements Menu, AfterContentInit, OnDestroy
5453
{
54+
private _focusMonitor = inject(FocusMonitor);
5555
protected ngZone = inject(NgZone);
5656
private _renderer = inject(Renderer2);
5757

@@ -108,13 +108,15 @@ export abstract class CdkMenuBase
108108
this.menuStack.push(this);
109109
}
110110
this._setKeyManager();
111+
this._handleFocus();
111112
this._subscribeToMenuStackHasFocus();
112113
this._subscribeToMenuOpen();
113114
this._subscribeToMenuStackClosed();
114115
this._setUpPointerTracker();
115116
}
116117

117118
ngOnDestroy() {
119+
this._focusMonitor.stopMonitoring(this.nativeElement);
118120
this.keyManager?.destroy();
119121
this.destroyed.next();
120122
this.destroyed.complete();
@@ -231,4 +233,18 @@ export abstract class CdkMenuBase
231233
this.menuAim.initialize(this, this.pointerTracker!);
232234
}
233235
}
236+
237+
/** Handles focus landing on the host element of the menu. */
238+
private _handleFocus() {
239+
this._focusMonitor
240+
.monitor(this.nativeElement, false)
241+
.pipe(takeUntil(this.destroyed))
242+
.subscribe(origin => {
243+
// Don't forward focus on mouse interactions, because it can
244+
// mess with the user's scroll position. See #30130.
245+
if (origin !== null && origin !== 'mouse') {
246+
this.focusFirstItem(origin);
247+
}
248+
});
249+
}
234250
}

0 commit comments

Comments
 (0)