Closed domenic closed 2 years ago
@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 RouterScroller
and supporting BrowserViewportScroller
that controls scrolling to the top of the page or an anchor on a "push" as well as restoring scroll position on a "traverse".
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 https://github.com/rafgraph/react-router-hash-link/blob/main/src/HashLink.jsx (https://github.com/remix-run/react-router/issues/394#issuecomment-220221604)
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.
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: https://github.com/remix-run/react-router/issues/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 https://github.com/WICG/navigation-api/issues/231#issuecomment-1138930254 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.