-
Notifications
You must be signed in to change notification settings - Fork 26
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
Making SPA-navs-with-hashes work #237
Comments
@domenic Thanks for creating an issue for this. I was recently investigating what we could do in the Angular router to resolve some of our scrolling issues. One of those options could be to include something that interacts with navigation API in a way that would eliminate a lot of the manual work we do for this. We have a whole A short investigation into some other routers indicated this manual handling is something other Router authors have encountered as well: https://sourcegraph.com/github.com/vuejs/router/-/blob/src/scrollBehavior.ts?L81-140 / https://sourcegraph.com/github.com/vuejs/router/-/blob/src/router.ts?L669-679 While there is certainly value in some of the customizability these options provide, it would be great if there was a browser primitive that could handle the most common use-cases. |
This changes the default behavior of intercepted navigations with respect to scrolling in the following ways: * Scroll restoration is performed on reloads, not just traverses. * For pushes and replaces, we either scroll to the top or scroll to the fragment. This change requires some updated API surface: the scrollRestoration option to intercept() has become scroll, and the navigateEvent.restoreScroll() method has become navigateEvent.scroll(). Closes #237 by giving an easy default behavior for SPA navigations with hashes. Closes #231 by making "push", "replace", and "reload" behave by default in an MPA-like manner with respect to scroll position, just like "traverse" does.
This changes the default behavior of intercepted navigations with respect to scrolling in the following ways: * Scroll restoration is performed on reloads, not just traverses. * For pushes and replaces, we either scroll to the top or scroll to the fragment. This change requires some updated API surface: the scrollRestoration option to intercept() has become scroll, and the navigateEvent.restoreScroll() method has become navigateEvent.scroll(). The latter method now has more capabilities, working to give browser-default-like behavior for "push", "replace", and "reload" in addition to "traverse". Similar to before, all of this can be opted out of by using scroll: "manual". Closes #237 by giving an easy default behavior for SPA navigations with hashes. Closes #231 by making "push", "replace", and "reload" behave by default in an MPA-like manner with respect to scroll position, just like "traverse" does.
This changes the default behavior of intercepted navigations with respect to scrolling in the following ways: * Scroll restoration is performed on reloads, not just traverses. * For pushes and replaces, we either scroll to the top or scroll to the fragment. This change requires some updated API surface: the scrollRestoration option to intercept() has become scroll, and the navigateEvent.restoreScroll() method has become navigateEvent.scroll(). The latter method now has more capabilities, working to give browser-default-like behavior for "push", "replace", and "reload" in addition to "traverse". Similar to before, all of this can be opted out of by using scroll: "manual". Closes #237 by giving an easy default behavior for SPA navigations with hashes. Closes #231 by making "push", "replace", and "reload" behave by default in an MPA-like manner with respect to scroll position, just like "traverse" does.
Scenario: you are on
/a
. You want to do a SPA navigation/b#hash
, by which you mean, load the contents for/b
, replace the DOM with them appropriately, and then scroll to the element withid="hash"
.This needs to be handled manually by SPA routers today, because they intercept the entire navigation process. See e.g. this issue: remix-run/react-router#394. The code in, e.g., https://github.com/rafgraph/react-router-hash-link is extremely complicated, but I think the basic idea is: let the router do its normal thing, then call
element.scrollIntoView()
withelement
based onlocation.hash
.The navigation API in its current form does not help. It makes it easy to avoid intercepting navigations from
/a
to/a#hash
. But navigations from/a
to/b#hash
will need to be intercepted withtransitionWhile()
, and once you do that, you lose any browser-native processing of following the fragment link.We could fix this. I think we should fix it at the same time as fixing #231, which is also about how scrolling should be handled on push/replace navigations. #231 envisioned resetting the scroll position after the push/replace "finishes", in the sense of
navigation.transition.finished
, after all the DOM for/b
is supposed to be loaded. Instead we should probably either reset the scroll position, or scroll to the location indicated by the hash.The main question is how to combine our three scroll-related configurable pieces of functionality. We have:
scrollRestoration
for"traverse"
navigations. (Should maybe also work for"reload"
.)"push"
/"replace"
navigations"push"
/"replace"
navigationsOne extreme is just
scroll: "after-transition" | "manual"
, unifyingscrollRestoration
with this new option. If you want to configure scroll restoration different than scroll resetting or scrolling-to-fragment, you need to investigatenavigateEvent.navigationType
and/ornavigateEvent.url
.Another is to keep with my argument in #231 (comment) for splitting up push/replace and traverse/reload, something like
scrollRestoration
for the former andinitialScroll
for the latter. Or maybescrollReset
is still a fine name for the latter; just likefocusReset
"resets" the focus to either the top, or to an explicitly-autofocus
ed element, soscrollReset
"resets" the scroll position to either the top, or to an explicitly-fragment-indicated element.And yet another is to allow configuring scroll-to-fragment behavior differently from no-fragment pushes, so e.g.
scrollRestoration
+scrollReset
+scrollFragmentHandling
.I'm starting to prefer just unifying them, though.
The text was updated successfully, but these errors were encountered: