Open Strepto opened 2 years ago
Thanks for that feedback!
I think it's worth noting that the Astro example you linked to is an MPA (multi-page app), not an SPA (single-page app). So the astro app is using the default browser scroll restoration on back navigation.
I think it would be great to understand what other SPA frameworks, like Remix, do in these cases.
I found something that the Remix team wrote in an earlier version of React Router (a tool they maintain which has an API for scroll restoration): https://v5.reactrouter.com/web/guides/scroll-restoration. One of the things it talks about is the idea of restoring scroll position on back/forward navigation, but not on link clicks.
They also talk about not implementing a generic solution, at least in that release. I'm still trying to understand if that is any different with the latest React Router release, or with Remix, or if you have to opt in to scroll restoration.
I think laying out how these things are solved in Remix would be a great starting point. In particular, I'd be curious to understand 1) what is the default behavior, and 2) what is the additional behavior that you can opt into or configure.
Here's the same hackernews demo built with Remix: https://remix-hackernews.ryansolid.workers.dev. It looks like it also has scroll restoration.
Looking through the code (https://github.com/ryansolid/remix-hackernews), the only reference I see to scrolling is here:
So it looks like the default Remix template includes that behavior, but they allow you to opt out of it by not using that ScrollRestoration component.
Here are the Remix docs on ScrollRestoration
: https://remix.run/docs/en/v1/api/remix#scrollrestoration.
It also looks like they try to run the scroll restoration behavior before hydrating the React app:
In order to avoid (usually) the client-side routing "scroll flash" on refresh or clicking back into the app from a different domain, this component attempts to restore scroll before React hydration. If you render the script anywhere other than the bottom of the document the window will not be tall enough to restore to the correct position.
Just out of curiosity I looked a bit at this and found this (see link below). Might it actively "work against" the browser default scroll state restoring?
I did not look at the code long enough so see if its triggered both on "link presses" and on "browser navigation", but it kinda feels like it does.
Maybe disabling this on browser history navigation could be a solution?
When running the hackernews demo locally (from branch serverless-latest
723f0b22a1901ece38fd9cb177664a5037454e56 ) I get the expected scroll position when navigating back. Not sure why its different, might be that it loads fast enough for the browser keep the scroll position while running from localhost?
Edit: now I'm confused as I cannot reproduce the localhost behaviour after doing some experimental changes and reverting them....
But adding a conditional ScrollToTop only if the navigation was from a link seems to work okish in my veeery limited testing
-- In Platform.elm UpdateCacheAndUrlNew
-- snip
, if fromLinkClick then
ScrollToTop
else
NoEffect
-- snip
makes it somewhat more like what I expect in my browser. But I have not tested for compability elsewhere, or with any other app than the hackernews demo.
My two cents from #437:
I noticed a few issues with how back navigation is handled with respect to scrolling.
For handling the scroll restauration on back navigation, react-router seems to follow this strategy:
history.scrollRestoration
to manual
to prevent clashes with the browser's behavior. More notes after some investigation (posted in the Discord):
I disabled the ScrollToTop by replacing this line with Task.perform (\_ -> PageScrollComplete) (Task.succeed ())
. Then I tested it by running the docs example.
Obviously, clicking on links is missing the scroll to top. E.g., if I open a blog article and scroll down a bit and then click on "Docs" link from the menu, it should open the docs page at the top, but instead it stays as far scrolled down as I was on the blog article. So after clicking on links, it should definitely scroll to the top.
Suprisingly, the browser (Firefox and Chrome) handles the back navigation pretty well after this code change. If I go to a blog article, scroll down, and go to the Docs and scroll down a different amount, pressing the browser's back and forward buttons does pretty much what I want.
However, the browser visibly scrolls before the new page is loaded. So it jumps down to the desired scroll position and then loads the new page. This leads to problem when the pages are different in height. E.g. when I scroll down completely on the docs page, go back to a shorter article, and then go forward again to the longer docs page, the browser scrolls first, hits the blog article's bottom and then switches to the docs page, which messes up the scroll position.
From the Elm debugger it seems that the browser scrolls after the UrlChanged message, but the page is only changed after the ProcessFetchResponse message. So it seems the problem lies with the separation between the back navigation, the fetching of the new page information and the resulting update.
I'm not sure that it is possible to "merge" those, so probably we should intercept the back navigation, kick off the fetch and once that returns trigger the back navigation. Similar to how after clicking on a link, we don't let the browser go there but fetch the data and then programmatically navigate.
Unfortunately, I don't know how to do this and I have a hunch that the browser may prevent hijacking the history navigation in this way. It seems that even with the proposed Navigation API, intercepting back and forwards navigation will not be allowed.
Hi, great stuff!
I have tried some of the demos, and notice that the vertical scroll position on a page is not kept when using the "Back" button in my browser. This makes apps such as the Hackernews clone (https://hacker-news-elm-pages.netlify.app) and the Elm-Pages website (https://elm-pages.com) harder to navigate.
Compare:
https://astro-solid-hn.netlify.app to https://hacker-news-elm-pages.netlify.app
Repro:
The astro-solid-hn keeps the scroll position from before you navigated away, while elm-pages-hn jumps to the top of the page again.
Browser: Vivaldi (Chromium based)
Feel free to close the issue if this is a duplicate, or a known problem :)