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 position reset on slow loading fastboot served pages #55

Closed
bcardarella opened this issue May 30, 2017 · 6 comments · Fixed by #169
Closed

scroll position reset on slow loading fastboot served pages #55

bcardarella opened this issue May 30, 2017 · 6 comments · Fixed by #169

Comments

@bcardarella
Copy link
Member

I know why this is happening, I'm not sure what the correct solution is yet.

If a page is served with Fastboot the immediate render is available. Then if Ember is slow to instantiate for any reason if you've scrolled down the page when the App starts the scroll position is reset. This is not the correct experience.

I suspect the correct thing to do is if the state is new assume the current scroll position rather than 0,0

@briangonzalez
Copy link
Contributor

@bcardarella I came to the same conclusion and implemented a small demo which I haven't pushed any where.

Are you 100% sure it's because of ember-router-scroll?

@bcardarella
Copy link
Member Author

@bcardarella
Copy link
Member Author

Keep in mind we can't simply assume the current x,y position of scroll here. One of the reqs of this lib is to reset the scroll position on each page transition, or reposition if it is in history.

So there needs to be a way to distinguish that this is an app instantiation event and ember is taking over from fastboot rendered page.

There could be a flag in an initializer for the service that is a hasStartedUp or something like that, defaulted to false. So on first read it will default to current browser's scroll position. On additional reads it will default to 0,0

@courthead
Copy link

courthead commented Jul 8, 2017

There are two issues. The first is what @bcardarella pointed out here, which is that this line is resetting the scroll position to 0,0 without taking into account where the user was scrolled before app instantiation happens.

However, even if you work around that, there's a second issue (also pointed out by @bcardarella
in #17) that causes a very unpleasant jump. Right at app instantiation, Ember erases all the HTML that came from FastBoot and renders its own HTML. That causes the app to scroll to the top of the page, and the router-scroll service's update function isn't called until didTransition happens a split second later.

Here's a temporary hack to fix both of these problems. I put the following in my top-level application route:

import { scheduleOnce } from 'ember-runloop';

export default Route.extend({
  setupController() {
    this._super(...arguments);

    if (!this.get('fastboot.isFastBoot')) {
      const routerScroll = this.get('router.service');
      routerScroll.set('key', window.history.state.uuid);
      routerScroll.update();

      scheduleOnce('afterRender', this, () => {
        const scrollPosition = routerScroll.get('position');
        window.scrollTo(scrollPosition.x, scrollPosition.y);
      });
    }
  },
});

@bcardarella
Copy link
Member Author

I gave this some thought a while ago, I think we should wait for DOM hydration before making any changes

@Duder-onomy
Copy link
Contributor

Duder-onomy commented Mar 6, 2018

Here is another workaround (until DOM hydration lands) for those getting here via Google.

The thinking is that we give the body a nice min-height that is equal to the users current scroll position plus the page height. Then when ember is done loading and rendering the initial route, we take that min-height away.

Add a script tag to a file you control, some vanilla JS.

<!--  index.html -->
{{content-for 'body'}}

<script src="{{rootURL}}assets/init-scroll.js"></script>
<!-- ^ this one is the important one, should be BEFORE ember scripts download -->

<script src="{{rootURL}}assets/vendor.js"></script>
// assets/init-scroll.js
(function() {
  if (typeof FastBoot === 'undefined') {
    document.body.style['min-height'] = document.body.clientHeight + 'px';
  }
}());

Finally, you revert the body min-height to something reasonable after your application route has finished its transition:

// routes/application.js
actions: {
    didTransition() {
        if (!get(this, 'fastboot.isFastBoot')) {
            run.scheduleOnce('afterRender', () => {
                document.body.style['min-height'] = '100vh';
            });
        }
    }
}

Its not great, obviously DOM hydration will be better. Figured it could help the next person that googles their way to this corner of github.

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.

4 participants