Skip to content
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

scroll other element than body on history navigation #2107

Open
breunigs opened this issue Jul 19, 2022 · 0 comments · May be fixed by #3051
Open

scroll other element than body on history navigation #2107

breunigs opened this issue Jul 19, 2022 · 0 comments · May be fixed by #3051

Comments

@breunigs
Copy link
Contributor

breunigs commented Jul 19, 2022

Currently, the scroll position for the body element is retained and restored on history navigation.

My specific use case is "prevent iOS overscroll", which is usually done by giving the body the height of the viewport, then wrapping all scrollable content in another div. I am aware that overscroll-behavior: contain supposedly fixes that in iOS 16, but at least in iOS 15 it won't work even with the experimental feature flag for this toggled on.

I guess there might be other use cases that scroll an element besides body, or maybe even have multiple scroll positions.

My initial attempt was to add my own scroll and popstate listeners, and modify the history state by adding my own attribute. This works in some cases, but there are edge cases left when navigating without another scroll event. During navigation the history state is rewritten by live view, removing my previously stored extra scroll position. I haven't fully understood why this happens, though I believe it's not due to a race condition with other scroll related code that gets delayed through requestAnimationFrame or setTimeout(…, 0). Even if I were to make that work, this approach more or less relies on live view working in an implicit way, rather than on a proper interface.

In terms of solutions, I can think of these approaches:

  1. make the element on which LiveView restores the scroll position configurable
  2. hooks/callbacks for client side code to store additional info into the history object and also one for popstate after LiveView did its thing
  3. re-store scroll position of all elements, or at least ones with a phx-restore-scroll attribute or similar
  4. store scroll positions outside of the history object, but with enough info to exactly match the pages in the forward/backward history (i.e. store the full state, not just the URL). This can be done on the server or client, as long as either one of them has all the information necessary for matching.

1 is easy but also very specific to my use case and doesn't generalize well.
2 would be powerful enough, but adding my own "workaround code" and generally writing more JS doesn't seem to match the intention of LiveView in general.
3 might be decent enough if limited to elements with an id attribute (+ special treatment for body). Having this to configure this at all strikes me as a downside though, since ideally I wouldn't have to think about scroll position restoring at all and navigating would "just restore all scroll positions"
4 sounds like duplication of state is necessary, or at least it'd require sending scroll events to the server. This seems a bit brittle and overkill at the same time.

Potentially some special handling for regular live_patching is required, too. If one of the "to be scrolled/restored" elements changes its contents, it will retain the scroll position. This is not what browsers do, and also a problem in my specific case (I exchange the article/text content this way, and it's unlikely that a user wants to starting reading a long from text from the middle). My current fix is a phx-hook that sets elem.scrollTop = 0, which I guess should be solved by any implementation. At the very least, there should be a defined way to ensure that these hooks are not racy with the scroll restoration implemented in LiveView itself.

Of all the approaches I mentioned, my guess is 3 is the least-worst. Naming will be tough if phx-restore-scroll also resets scroll when using regular/non-history live_patch navigation. Doing it for all elements that have an id and receive a scroll event might also work, but has the surprising side-effect that the scroll behaviour changes when this attribute is added/removed. Given that it's probably not a common issue, I'd rather go for opt-in because that can be searched for in the documentation.

I'm happy to work on this because this annoys me to a greater deal than I am willing to admit. Given the unclear trade-off situation, I am hoping a second pair of eyes can provide a better suggestion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant