-
Notifications
You must be signed in to change notification settings - Fork 26.3k
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
feat(docs-infra): saves the scroll position before the change of loca… #28037
Conversation
Fixes #17308 |
We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google. |
Hi @petebacondarwin , |
You can preview 4ecf896 at https://pr28037-4ecf896.ngbuilds.io/. |
@wKoza - it seems that your second commit came from a different account to the first commit, and does not have CLA approval. Could you squash the commits into one committed by the appropriate account? |
One problem I note immediately in the preview is that this is not working when you use the forward button to navigate. |
9ac71d8
to
4ecf896
Compare
CLAs look good, thanks! |
It's Ok with the CLAs. Can you give me an example of reproduction steps ? |
|
We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google. |
b9407ca
to
f10e0d8
Compare
CLAs look good, thanks! |
It was a flake! So if you don't mind squashing these commits (as you see fit) and then we'll double-check with @IgorMinar before merging. Great work! |
5bcda90
to
a9d0785
Compare
You can preview a9d0785 at https://pr28037-a9d0785.ngbuilds.io/. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry to crash the party, but I was passing by and thought I'd add some comments 😁
The implementation seems a little complicated (I am not even 100% sure how it works 😁), but the end result looks correct ✔️
Nice one, @wKoza 👍
// The previous document has been removed. | ||
// Scroll to top to restore a clean visual state for the new document. | ||
this.scrollService.scrollToTop(); | ||
this.scrollService.removeStoredScrollPosition(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why remove the comment from here? I think this is the right place for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name of the method is onDocRemoved
. I think the first line is redundant.
The second line have to be removed. We stop to scrollToTop() when the Doc is removed.
this.location.subscribe(state => { | ||
if (state.type === 'hashchange') { | ||
this.popStateFired = false; | ||
this.scrollToPosition(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to call scrolToPosition()
here? How was this handled before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean here? If so, why do we need both calls?
More importantly, why do we need to scroll to this.scrollPosition
, whent the hash changes (and not scroll to the target element or to the top)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
But doesn't hashchange
mean we are still on the same page (and only the hash has changed)? This path shouldn't be hit when we go back, right?
Choose a reason for hiding this comment
The 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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand in what circumstances this could have an effect.
AFAICT, this line is enough for scrolling to the top. It seems to me that one of the two has to go. Am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The 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 comment
The 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.
Let's leave it as is for now (since it seems to work fine). We can always tweak it later is needed 😉
aio/src/app/shared/scroll.service.ts
Outdated
} else { | ||
this.popStateFired = true; | ||
// we always should have a scrollPosition in our state history | ||
this.scrollPosition = state.state ? state.state['scrollPosition'] : null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is an edge-case, but I believe we should store the new scrollPosition
to sessionStorage
. As it is now, if the user reloads before the 500ms delay, they will end up to the previous page's scroll position.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, we doesn't have a delay. You can see this line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scrolling is synchronous in scrollAfterDelay()
(which is not a great name for a method that scrolls synchronously 😛), but calling scrollAfterDelay()
is not synchronous wrt this line here. I was able to reproduce this by clicking the back button and reloading quickly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your comment is outofdate. The name is now scrollAfterRender() 😇.
I 'm not able to reproduce this. For me, It would mean rather that the event onDocRemoved
was been fired .
@petebacondarwin , finally, the setTimeOut here seems to be unnecessary. I'll remove it. |
OK, great! |
You can preview 09b3d1f at https://pr28037-09b3d1f.ngbuilds.io/. |
@@ -348,6 +342,9 @@ export class AppComponent implements OnInit { | |||
// Dynamically change height of table of contents container | |||
@HostListener('window:scroll') | |||
onScroll() { | |||
|
|||
this.scrollService.updateScrollPositionInHistory(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since updateScrollPositionInHistory()
isn't trivial, it might be a good idea to do some debouncing here. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had asked me the question when I wanted to use the scroll event. For the moment, I did not feel any lag. But, it is an option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On Chrome (desktop), after some scrolling, I start to get the following warnings in the console:
Throttling history state changes to prevent the browser from hanging.
So, I believe it is a good idea to do some throttling/debouncing here.
this.location.subscribe(state => { | ||
if (state.type === 'hashchange') { | ||
this.popStateFired = false; | ||
this.scrollToPosition(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean here? If so, why do we need both calls?
More importantly, why do we need to scroll to this.scrollPosition
, whent the hash changes (and not scroll to the target element or to the top)?
@gkalpak, I've covered all of your questions. Notify me before my (last?) commit ;) |
@wKoza, I think you are good to go for that (last?) commit 💪 |
You can preview bf6bc41 at https://pr28037-bf6bc41.ngbuilds.io/. |
|
||
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 comment
The 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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
It seems redundant (unless there are environments where window
is defined and has a scrollTo
getter, but trying to access window.scrollTo
throws an error.
(Not that I haven't seen such strange limitations in certain environments 😉 angular/angular.js#13945)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome work, @wKoza 💯
Thx so much ❤️ ❤️ ❤️
BTW, I have created #28332 to track possible improvements to scroll position restoration (but nothing that should block this PR) 😉 |
🎉 |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
…tion or after a reload
closes #27916
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Issue Number: N/A
What is the new behavior?
Does this PR introduce a breaking change?
Other information