@@ -17,6 +17,8 @@ export class ScrollService {
17
17
popStateFired = false ;
18
18
// scroll position which has to be restored after the popState event
19
19
scrollPosition : [ number , number ] = [ 0 , 0 ] ;
20
+ // true when the browser supports `scrollTo`, `scrollX`, `scrollY` and `scrollRestoration`
21
+ supportManualScrollRestoration : boolean ;
20
22
21
23
// Offset from the top of the document to bottom of any static elements
22
24
// at the top (e.g. toolbar) + some margin
@@ -43,18 +45,29 @@ export class ScrollService {
43
45
// On resize, the toolbar might change height, so "invalidate" the top offset.
44
46
fromEvent ( window , 'resize' ) . subscribe ( ( ) => this . _topOffset = null ) ;
45
47
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
+
46
55
// Change scroll restoration strategy to `manual` if it's supported
47
- if ( this . supportManualScrollRestoration ( ) ) {
56
+ if ( this . supportManualScrollRestoration ) {
48
57
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' ) {
52
63
this . popStateFired = false ;
53
64
this . scrollToPosition ( ) ;
54
65
} 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`.
55
68
this . popStateFired = true ;
56
69
// 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 ;
58
71
}
59
72
} ) ;
60
73
}
@@ -82,23 +95,27 @@ export class ScrollService {
82
95
83
96
/**
84
97
* 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
86
99
* @param delay before we scroll to the good position
87
100
*/
88
101
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 ( ) ! ) ;
91
105
} else {
92
106
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.
93
109
if ( this . isLocationWithHash ( ) ) {
94
110
// Scroll 500ms after the new document has been inserted into the doc-viewer.
95
111
// The delay is to allow time for async layout to complete.
96
112
setTimeout ( ( ) => this . scroll ( ) , delay ) ;
97
113
} else {
114
+ // If the location doesn't contain a hash, we scroll to the top of the page.
98
115
this . scrollToTop ( ) ;
99
116
}
100
117
} 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
102
119
this . scrollToPosition ( ) ;
103
120
}
104
121
}
@@ -142,7 +159,7 @@ export class ScrollService {
142
159
* Update the state with scroll position into history.
143
160
*/
144
161
updateScrollPositionInHistory ( ) {
145
- if ( this . supportManualScrollRestoration ( ) ) {
162
+ if ( this . supportManualScrollRestoration ) {
146
163
const currentScrollPosition = this . viewportScroller . getScrollPosition ( ) ;
147
164
this . location . replaceState ( this . location . path ( true ) , undefined , { scrollPosition : currentScrollPosition } ) ;
148
165
window . sessionStorage . setItem ( 'scrollPosition' , currentScrollPosition . toString ( ) ) ;
@@ -159,21 +176,10 @@ export class ScrollService {
159
176
}
160
177
161
178
/**
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
174
180
*/
175
181
needToFixScrollPosition ( ) : boolean {
176
- return this . popStateFired && this . scrollPosition && this . supportManualScrollRestoration ( )
182
+ return this . popStateFired && this . scrollPosition && this . supportManualScrollRestoration ;
177
183
}
178
184
179
185
/**
0 commit comments