mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
fix(common): prevent focus from scrollToAnchor
Focus the target element using `focus({preventScroll: true})` after scrolling, so the browser doesn’t adjust the scroll position when applying focus.
Fixes #65938
(cherry picked from commit 97cac1cf4d)
This commit is contained in:
parent
d07f502946
commit
10ad3c0692
2 changed files with 30 additions and 4 deletions
|
|
@ -55,6 +55,7 @@ export abstract class ViewportScroller {
|
|||
/**
|
||||
* Scrolls to an anchor element.
|
||||
* @param anchor The ID of the anchor element.
|
||||
* @param options Scroll options
|
||||
*/
|
||||
abstract scrollToAnchor(anchor: string, options?: ScrollOptions): void;
|
||||
|
||||
|
|
@ -125,11 +126,12 @@ export class BrowserViewportScroller implements ViewportScroller {
|
|||
this.scrollToElement(elSelected, options);
|
||||
// After scrolling to the element, the spec dictates that we follow the focus steps for the
|
||||
// target. Rather than following the robust steps, simply attempt focus.
|
||||
//
|
||||
// Use `preventScroll: true` to avoid extra scroll that breaks smooth scrolling.
|
||||
// @see https://html.spec.whatwg.org/#get-the-focusable-area
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/focus
|
||||
// @see https://html.spec.whatwg.org/#focusable-area
|
||||
elSelected.focus();
|
||||
// @see https://www.yanandcoffee.com/2020/05/08/accessible-smooth-scrolling-and-focus-management-solutions/
|
||||
elSelected.focus({preventScroll: true});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -235,7 +237,7 @@ export class NullViewportScroller implements ViewportScroller {
|
|||
/**
|
||||
* Empty implementation
|
||||
*/
|
||||
scrollToAnchor(anchor: string): void {}
|
||||
scrollToAnchor(anchor: string, options?: ScrollOptions): void {}
|
||||
|
||||
/**
|
||||
* Empty implementation
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import {BrowserViewportScroller, ViewportScroller} from '../src/viewport_scroller';
|
||||
import {isNode} from '@angular/private/testing';
|
||||
import {isNode, waitFor} from '@angular/private/testing';
|
||||
|
||||
describe('BrowserViewportScroller', () => {
|
||||
describe('setHistoryScrollRestoration', () => {
|
||||
|
|
@ -112,6 +112,30 @@ describe('BrowserViewportScroller', () => {
|
|||
cleanup();
|
||||
});
|
||||
|
||||
it('should honor the scroll offset when smooth scrolling', async () => {
|
||||
// Ensure the scroll behavior is smooth for this test, as the bug only occurred with smooth scrolling.
|
||||
document.documentElement.style.scrollBehavior = 'smooth';
|
||||
|
||||
const {anchorNode, cleanup} = createTallElement();
|
||||
anchorNode.id = anchor;
|
||||
|
||||
// Padding ensures the page is tall enough that the offset-adjusted target
|
||||
// is reachable and not clamped to the maximum scroll position.
|
||||
document.body.style.paddingBottom = '5000px';
|
||||
// Header offset
|
||||
scroller.setOffset([0, 80]);
|
||||
|
||||
scroller.scrollToAnchor(anchor);
|
||||
|
||||
await waitFor(() => throwUnless(anchorNode.getBoundingClientRect().top).toBe(80), {
|
||||
timeout: 1_000,
|
||||
});
|
||||
|
||||
document.documentElement.style.scrollBehavior = '';
|
||||
document.body.style.paddingBottom = '';
|
||||
cleanup();
|
||||
});
|
||||
|
||||
function createTallElement() {
|
||||
const tallItem = document.createElement('div');
|
||||
tallItem.style.height = '3000px';
|
||||
|
|
|
|||
Loading…
Reference in a new issue