router icon indicating copy to clipboard operation
router copied to clipboard

Restore scroll position on navigation

Open vlukashov opened this issue 6 years ago • 17 comments

As Flow I want Vaadin.Router to support restoring scroll position on navigation in the same way it works in Flow 1.0 so that the future versions of Vaadin Flow could switch to Vaadin.Router instead of the own implementation.

NOTE: react-router does not have any explicit support for scroll position restoration. Their position is that browsers do that by default quite well already: https://reacttraining.com/react-router/web/guides/scroll-restoration

vlukashov avatar Apr 25 '18 11:04 vlukashov

A reference to the pwa-helpers router: https://github.com/Polymer/pwa-helpers/pull/14

abdonrd avatar Jun 12 '18 08:06 abdonrd

Any option similar to https://github.com/Polymer/pwa-helpers/pull/14? Because the event.type is always vaadin-router-location-changed.

abdonrd avatar Feb 16 '19 14:02 abdonrd

do we have some kind of advance for this issue?

Tansito avatar Jul 30 '19 09:07 Tansito

Related example: https://stackblitz.com/edit/lit-element-demo-zejjsv

Steps:

  1. Go to home and scroll to bottom
  2. Click on Sam's profile Now the scroll go to the top
  3. Scroll to the bottom and click on Home The scroll remains

It seems that the problem is the lazy load of the view using dynamic import().

Now If you uncomment the static imports, it always keeps the scroll:

import './views/home-view.js';
import './views/profile-view.js';
import './views/not-found-view.js';

abdonrd avatar Aug 09 '19 10:08 abdonrd

There are any workaround with this? Thanks in advance!

abdonrd avatar Oct 22 '19 14:10 abdonrd

One way to reset scroll position would be to add a listener for the vaadin-router-location-changed event:

window.addEventListener('vaadin-router-location-changed', e => {
  window.scrollTo(0, 0);
});

A more elaborate solution would require changes in the Router APIs. I can think of two different use cases. Does any of that look relevant to your app?

  • if you want to reset scroll position when navigation is triggered by clicking a link, but not when it's triggered by the browser 'back' button That would require passing some information from the navigation trigger along with the event so that it's possible to write the code like
    window.addEventListener('vaadin-router-location-changed', e => {
      if (e.detail.trigger === 'click') {
        window.scrollTo(0, 0);
      }
    });
    
  • if you want to save and restore the exact scroll position for each route That would require keeping track of the history of scroll positions for each route and passing that along with the event so that it's possible to write the code like
    window.addEventListener('vaadin-router-location-changed', e => {
      if (e.detail.savedScrollPosition) {
        window.scrollTo(e.detail.savedScrollPosition.x, e.detail.savedScrollPosition.y);
      }
    });
    

vlukashov avatar Oct 24 '19 06:10 vlukashov

Thanks for the answer, @vlukashov!

I discard the first option because in many situations we shouldn't scroll to top.

As before I was using the Polymer/pwa-helpers router, the second option seems really interesting! Easy and simple. But I don't have a strong opinion. Also the third looks nice.

abdonrd avatar Oct 24 '19 07:10 abdonrd

Adding a link to the Vue router implementation for reference.

web-padawan avatar Feb 19 '20 08:02 web-padawan

I think we should use the state serializable object in the pushState API, each time router calls to the method should pass the scroll position, then on popState it can be restored. Not sure about how to make it configurable by the user in order to configure which routes needs to store/restore scrolling but could be an extra property in the route entry

manolo avatar Feb 19 '20 11:02 manolo

Note: as discussed today with https://vaadin.com team, we might try history.scrollPosition: https://caniuse.com/#feat=mdn-api_history_scrollrestoration

It supports all the target browsers of Vaadin 15 (don't be confused with Edge 80, the data seems to be incorrect). IMO this is something worth prototyping to see if it works for us.

web-padawan avatar Feb 19 '20 11:02 web-padawan

Just opening up a discussion here: Should we consider the use case of scrolling to an anchor in the page instead of a scroll position?

miladkdz avatar Feb 21 '20 10:02 miladkdz

Any news here?

abdonrd avatar Apr 13 '20 21:04 abdonrd

Very interested too in this feature and in the scroll to anchor from @miladkdz

Tansito avatar Apr 27 '20 12:04 Tansito

Hi, any updates on this issue?

ruanxianzhi avatar May 05 '20 19:05 ruanxianzhi

Not sure if I have a special case, but for me scroll is restored properly on page changes, but when I go to a new page, it doesn't move the scroll position to the top. So I'm pretty confused.

At the very least, if we had something like e.detail.originalEvent with the "vaadin-router-location-changed" event, then we could inspect it and do what we like.

For example, change src/triggers/popstate.js to:

function vaadinRouterGlobalPopstateHandler(originalEvent) {
  if (originalEvent.state === 'vaadin-router-ignore') {
    return;
  }
  const {pathname, search, hash} = window.location;
  fireRouterEvent('go', {pathname, search, hash, originalEvent});
}

And then do something similar in vaadinRouterGlobalClickHandler() as well, now we can see if the event came from a click or popstate, and go from there.

Ultimately, I think saving and restoring the scroll position should be a top priority. I've used the router in a few places, and this issue is mysteriously present or not, and I can't figure out why. When it is present, it is a debilitating user experience.

DeadWisdom avatar May 14 '20 21:05 DeadWisdom

Hello, any updates?👀

MuuKojima avatar Jul 17 '20 17:07 MuuKojima

It's not ideal, but we have a simple patch to deal with it here: https://github.com/IBM/pwa-lit-template/issues/1

abdonrd avatar Jul 20 '20 13:07 abdonrd