I have found a similar problem in several web applications I’ve done in Elm. I think there are multiple strategies for dealing with this problem and wondered if anyone had any experience to share. The problem is the scroll bar position when navigating between “pages” within a single-page-application.
I think this mostly occurs when you have a shared ‘frame’, or ‘shell’, which is typically say a header bar, a menu, and perhaps a footer as well. So suppose you have an application that has a list of news article headlines, each of which you can click to read the entire article. You have a shell consisting of a header bar with a logo and maybe a profile button (login when not logged-in say), and a side menu bar. So your view function might look something like (obviously leaving out many details):
view : Model -> Html Msg view model = let mainContent = case model.route of Home -> Html.ul [ Attributes.class "headline-list" ] (List.map viewHeadline model.articles) Article articleId -> let article = getArticleSomehow ... in div [ Attributes.class "article" ] [ viewArticleTitle article.title , viewArticleContent article.content ] in div [ viewHeader , viewNav , mainContent ]
Now presumably each of the article headlines contain a link to the url for just that single article, and you do the usual thing to update the route upon a
UrlChangeRequest. Similarly presumably there is way to go back from a whole article to the feed.
The problem is, if the headline list is long and you scroll down that list of headlines, when you click on a headline you are taken to the article page, but the scroll is remembered, so you are half way down the article when you should be at the top. In addition if you scroll up to the top of the article, and go ‘back’ (possibly via a back link in the application), then you are taken back to the list of headlines but the scroll position is wherever you were on the whole article page (perhaps at the very top).
I can think of a couple of solutions to this:
- On a route change, get the current viewport and remember it for the current route. For the new route you need to decide if you want to either set the scroll position to some remembered position, or reset it to the top.
- Have the ‘shell’ take up exactly 100% of the screen, and have the main-content part scrollable within that shell. I have tried this in the past and found it to be somewhat of a mine-field of corner-cases and different device/browser behaviours.
- Have the whole article page as an overlay and keep the main headline list underneath it. I’ve tried this approach before and it also has some device/browser behaviour differences, but fewer than case number 2.
So I guess I would like to hear of anyone’s experience with this issue. Any other solutions I haven’t thought of? Any advantages/disadvantages of any solution? I suspect the preferred solution will be largely dependent on the exact use-case, but would love to hear any related thoughts/comments.