-
Notifications
You must be signed in to change notification settings - Fork 26.5k
feat(docs-infra): saves the scroll position before the change of loca… #28037
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
…of location
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,8 @@ export class ScrollService { | |
popStateFired = false; | ||
// scroll position which has to be restored after the popState event | ||
scrollPosition: [number, number] = [0, 0]; | ||
// true when the browser supports `scrollTo`, `scrollX`, `scrollY` and `scrollRestoration` | ||
supportManualScrollRestoration: boolean; | ||
|
||
// Offset from the top of the document to bottom of any static elements | ||
// at the top (e.g. toolbar) + some margin | ||
|
@@ -43,18 +45,29 @@ export class ScrollService { | |
// On resize, the toolbar might change height, so "invalidate" the top offset. | ||
fromEvent(window, 'resize').subscribe(() => this._topOffset = null); | ||
|
||
try { | ||
this.supportManualScrollRestoration = !!window && !!window.scrollTo && 'scrollX' in window | ||
&& 'scrollY' in window && !!history && !!history.scrollRestoration; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OOC, under what circumstances do we expect this line to throw? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems redundant (unless there are environments where (Not that I haven't seen such strange limitations in certain environments 😉 angular/angular.js#13945) |
||
} catch { | ||
this.supportManualScrollRestoration = false; | ||
} | ||
|
||
// Change scroll restoration strategy to `manual` if it's supported | ||
if (this.supportManualScrollRestoration()) { | ||
if (this.supportManualScrollRestoration) { | ||
history.scrollRestoration = 'manual'; | ||
// we have to detect forward and back navigation | ||
this.location.subscribe(state => { | ||
if (state.type === 'hashchange') { | ||
// we have to detect forward and back navigation thanks to popState event | ||
this.location.subscribe(event => { | ||
// the type is `hashchange` when the fragment identifier of the URL has changed. It allows us to go to position | ||
// just before a click on an anchor | ||
if (event.type === 'hashchange') { | ||
this.popStateFired = false; | ||
wKoza marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this.scrollToPosition(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Before, we called autoscroll() if I recall There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean here? If so, why do we need both calls? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After, when a location had a hash, we called autoscroll() to scroll to the target element. Now, we can go back and return to a location which has a hash BUT, we can be to a another position (we have scrolled after have been to target element (anchor)). It's clear ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But doesn't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, but wouldn't the scrolling be taken care of this line in that case? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes ,This line is called at the last step but it scrolls to the top since there is no more hash. But I'm agree, sometimes, this line is called for nothing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand in what circumstances this could have an effect. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. I played with this locally and the emitted events are very different from what I expected. |
||
} else { | ||
// The popstate event is always triggered by doing a browser action such as a click on the back or forward button. | ||
// It can be follow by a event of type `hashchange`. | ||
this.popStateFired = true; | ||
// we always should have a scrollPosition in our state history | ||
this.scrollPosition = state.state ? state.state['scrollPosition'] : null; | ||
this.scrollPosition = event.state ? event.state['scrollPosition'] : null; | ||
} | ||
}); | ||
} | ||
|
@@ -82,23 +95,27 @@ export class ScrollService { | |
|
||
/** | ||
* When we load a document, we have to scroll to the correct position depending on whether this is a new location | ||
* or a back/forward in the history | ||
* , a back/forward in the history, or a refresh | ||
wKoza marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* @param delay before we scroll to the good position | ||
*/ | ||
scrollAfterRender(delay: number) { | ||
if (this.getStoredScrollPosition()) { | ||
setTimeout(() => this.viewportScroller.scrollToPosition(this.getStoredScrollPosition() !), delay); | ||
// If we do rendering following a refresh, we use the scroll Position from the storage. | ||
wKoza marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (this.getStoredScrollPosition()) { | ||
wKoza marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this.viewportScroller.scrollToPosition(this.getStoredScrollPosition() !); | ||
wKoza marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} else { | ||
if (!this.needToFixScrollPosition()) { | ||
wKoza marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// The document was reloaded following a link. If the location contains a hash, we have to wait for async | ||
wKoza marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// layout. | ||
if (this.isLocationWithHash()) { | ||
// Scroll 500ms after the new document has been inserted into the doc-viewer. | ||
// The delay is to allow time for async layout to complete. | ||
setTimeout(() => this.scroll(), delay); | ||
} else { | ||
// If the location doesn't contain a hash, we scroll to the top of the page. | ||
this.scrollToTop(); | ||
} | ||
} else { | ||
// The document was reloaded following a popState` event`, so we manage the scroll scrollPosition | ||
// The document was reloaded following a popState `event`, so we manage the scroll scrollPosition | ||
wKoza marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this.scrollToPosition(); | ||
} | ||
} | ||
|
@@ -142,7 +159,7 @@ export class ScrollService { | |
* Update the state with scroll position into history. | ||
*/ | ||
updateScrollPositionInHistory() { | ||
if (this.supportManualScrollRestoration()) { | ||
if (this.supportManualScrollRestoration) { | ||
const currentScrollPosition = this.viewportScroller.getScrollPosition(); | ||
th 8000 is.location.replaceState(this.location.path(true), undefined, {scrollPosition: currentScrollPosition}); | ||
window.sessionStorage.setItem('scrollPosition', currentScrollPosition.toString()); | ||
|
@@ -159,21 +176,10 @@ export class ScrollService { | |
} | ||
|
||
/** | ||
* Check if the browser support `scrollTo` and `scrollRestoration` | ||
*/ | ||
supportManualScrollRestoration(): boolean { | ||
try { | ||
return !!window && !!window.scrollTo && 'scrollX' in window && 'scrollY' in window && !!history && !!history.scrollRestoration; | ||
} catch { | ||
return false; | ||
} | ||
} | ||
|
||
/** | ||
* Check if the scroll positon need to manually fix after popState event | ||
* Check if the scroll position need to be manually fixed after popState event | ||
*/ | ||
needToFixScrollPosition(): boolean { | ||
return this.popStateFired && this.scrollPosition && this.supportManualScrollRestoration() | ||
return this.popStateFired && this.scrollPosition && this.supportManualScrollRestoration; | ||
} | ||
|
||
/** | ||
|
Uh oh!
There was an error while loading. Please reload this page.