8000 fixup! feat(docs-infra): saves the scroll position before the change … · angular/angular@09b3d1f · GitHub
[go: up one dir, main page]

Skip to content

Commit 09b3d1f

Browse files
committed
fixup! feat(docs-infra): saves the scroll position before the change of location
1 parent a9d0785 commit 09b3d1f

File tree

3 files changed

+35
-36
lines changed

3 files changed

+35
-36
lines changed

aio/src/app/app.component.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export class AppComponent implements OnInit {
127127
}
128128
if (path === this.currentPath) {
129129
// scroll only if on same page (most likely a change to the hash)
130-
this.autoScroll();
130+
this.scrollService.scroll();
131131
} else {
132132
// don't scroll; leave that to `onDocRendered`
133133
this.currentPath = path;
@@ -187,11 +187,6 @@ export class AppComponent implements OnInit {
187187
.subscribe(() => this.updateShell());
188188
}
189189

190-
// Scroll to the anchor in the hash fragment or top of doc.
191-
autoScroll() {
192-
this.scrollService.scroll();
193-
}
194-
195190
onDocReady() {
196191
// About to transition to new view.
197192
this.isTransitioning = true;
@@ -253,7 +248,7 @@ export class AppComponent implements OnInit {
253248

254249
@HostListener('click', ['$event.target', '$event.button', '$event.ctrlKey', '$event.metaKey', '$event.altKey'])
255250
onClick(eventTarget: HTMLElement, button: number, ctrlKey: boolean, metaKey: boolean, altKey: boolean): boolean {
256-
251+
// We update the scroll position in the case there is no scroll event on the page before
257252
this.scrollService.updateScrollPositionInHistory();
258253

259254
// Hide the search results if we clicked outside both the "search box" and the "search results"

aio/src/app/shared/scroll.service.spec.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describe('ScrollService', () => {
5555
});
5656

5757
it('should set `scrollRestoration` to `manual` if supported', () => {
58-
if (scrollService.supportManualScrollRestoration()) {
58+
if (scrollService.supportManualScrollRestoration) {
5959
expect(window.history.scrollRestoration).toBe('manual');
6060
} else {
6161
expect(window.history.scrollRestoration).toBeUndefined();
@@ -246,7 +246,7 @@ describe('ScrollService', () => {
246246
it('should return true when popState event was fired after a back navigation if the browser supports ' +
247247
'scrollRestoration`. Otherwise, needToFixScrollPosition() returns false', () => {
248248

249-
if (scrollService.supportManualScrollRestoration()) {
249+
if (scrollService.supportManualScrollRestoration) {
250250
location.go('/initial-url1');
251251
// We simulate a scroll down
252252
location.replaceState('/initial-url1', 'hack', {scrollPosition: [2000, 0]});
@@ -271,7 +271,7 @@ describe('ScrollService', () => {
271271
it('should return true when popState event was fired after a forward navigation if the browser supports ' +
272272
'scrollRestoration`. Otherwise, needToFixScrollPosition() returns false', () => {
273273

274-
if (scrollService.supportManualScrollRestoration()) {
274+
if (scrollService.supportManualScrollRestoration) {
275275
location.go('/initial-url1');
276276
location.go('/initial-url2');
277277
// We simulate a scroll down
@@ -348,10 +348,8 @@ describe('ScrollService', () => {
348348

349349
scrollService.scrollAfterRender(scrollDelay);
350350

351-
expect(viewportScrollerStub.scrollToPosition).not.toHaveBeenCalled();
352-
expect(getStoredScrollPositionSpy).toHaveBeenCalled();
353-
tick(scrollDelay);
354351
expect(viewportScrollerStub.scrollToPosition).toHaveBeenCalled();
352+
expect(getStoredScrollPositionSpy).toHaveBeenCalled();
355353
}));
356354

357355
it('should call `scrollToPosition` after a popState', fakeAsync(() => {

aio/src/app/shared/scroll.service.ts

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export class ScrollService {
1717
popStateFired = false;
1818
// scroll position which has to be restored after the popState event
1919
scrollPosition: [number, number] = [0, 0];
20+
// true when the browser supports `scrollTo`, `scrollX`, `scrollY` and `scrollRestoration`
21+
supportManualScrollRestoration: boolean;
2022

2123
// Offset from the top of the document to bottom of any static elements
2224
// at the top (e.g. toolbar) + some margin
@@ -43,18 +45,29 @@ export class ScrollService {
4345
// On resize, the toolbar might change height, so "invalidate" the top offset.
4446
fromEvent(window, 'resize').subscribe(() => this._topOffset = null);
4547

48+
try {
49+
this.supportManualScrollRestoration = !!window && !!window.scrollTo && 'scrollX' in window
50+
&& 'scrollY' in window && !!history && !!history.scrollRestoration;
51+
} catch {
52+
this.supportManualScrollRestoration = false;
53+
}
54+
4655
// Change scroll restoration strategy to `manual` if it's supported
47-
if (this.supportManualScrollRestoration()) {
56+
if (this.supportManualScrollRestoration) {
4857
history.scrollRestoration = 'manual';
49-
// we have to detect forward and back navigation
50-
this.location.subscribe(state => {
51-
if (state.type === 'hashchange') {
58+
// we have to detect forward and back navigation thanks to popState event
59+
this.location.subscribe(event => {
60+
// the type is `hashchange` when the fragment identifier of the URL has changed. It allows us to go to position
61+
// just before a click on an anchor
62+
if (event.type === 'hashchange') {
5263
this.popStateFired = false;
5364
this.scrollToPosition();
5465
} else {
66+
// The popstate event is always triggered by doing a browser action such as a click on the back or forward button.
67+
// It can be follow by a event of type `hashchange`.
5568
this.popStateFired = true;
5669
// we always should have a scrollPosition in our state history
57-
this.scrollPosition = state.state ? state.state['scrollPosition'] : null;
70+
this.scrollPosition = event.state ? event.state['scrollPosition'] : null;
5871
}
5972
});
6073
}
@@ -82,23 +95,27 @@ export class ScrollService {
8295

8396
/**
8497
* When we load a document, we have to scroll to the correct position depending on whether this is a new location
85-
* or a back/forward in the history
98+
* , a back/forward in the history, or a refresh
8699
* @param delay before we scroll to the good position
87100
*/
88101
scrollAfterRender(delay: number) {
89-
if (this.getStoredScrollPosition()) {
90-
setTimeout(() => this.viewportScroller.scrollToPosition(this.getStoredScrollPosition() !), delay);
102+
// If we do rendering following a refresh, we use the scroll Position from the storage.
103+
if (this.getStoredScrollPosition()) {
104+
this.viewportScroller.scrollToPosition(this.getStoredScrollPosition() !);
91105
} else {
92106
if (!this.needToFixScrollPosition()) {
107+
// The document was reloaded following a link. If the location contains a hash, we have to wait for async
108+
// layout.
93109
if (this.isLocationWithHash()) {
94110
// Scroll 500ms after the new document has been inserted into the doc-viewer.
95111
// The delay is to allow time for async layout to complete.
96112
setTimeout(() => this.scroll(), delay);
97113
} else {
114+
// If the location doesn't contain a hash, we scroll to the top of the page.
98115
this.scrollToTop();
99116
}
100117
} else {
101-
// The document was reloaded following a popState` event`, so we manage the scroll scrollPosition
118+
// The document was reloaded following a popState `event`, so we manage the scroll scrollPosition
102119
this.scrollToPosition();
103120
}
104121
}
@@ -142,7 +159,7 @@ export class ScrollService {
142159
* Update the state with scroll position into history.
143160
*/
144161
updateScrollPositionInHistory() {
145-
if (this.supportManualScrollRestoration()) {
162+
if (this.supportManualScrollRestoration) {
146163
const currentScrollPosition = this.viewportScroller.getScrollPosition();
147164
this.location.replaceState(this.location.path(true), undefined, {scrollPosition: currentScrollPosition});
148165
window.sessionStorage.setItem('scrollPosition', currentScrollPosition.toString());
@@ -159,21 +176,10 @@ export class ScrollService {
159176
}
160177

161178
/**
162-
* Check if the browser support `scrollTo` and `scrollRestoration`
163-
*/
164-
supportManualScrollRestoration(): boolean {
165-
try {
166-
return !!window && !!window.scrollTo && 'scrollX' in window && 'scrollY' in window && !!history && !!history.scrollRestoration;
167-
} catch {
168-
return false;
169-
}
170-
}
171-
172-
/**
173-
* Check if the scroll positon need to manually fix after popState event
179+
* Check if the scroll position need to be manually fixed after popState event
174180
*/
175181
needToFixScrollPosition(): boolean {
176-
return this.popStateFired && this.scrollPosition && this.supportManualScrollRestoration()
182+
return this.popStateFired && this.scrollPosition && this.supportManualScrollRestoration;
177183
}
178184

179185
/**

0 commit comments

Comments
 (0)
0