DEV Community

Cover image for Support for in/inter page linking / scrolling in EmberJS
Michal Bryxí
Michal Bryxí

Posted on

Support for in/inter page linking / scrolling in EmberJS

The problem

Navigating to URLs with #hash-targets in them is not supported by most single-page-app frameworks due to the async rendering nature of modern web apps -- the browser can't scroll to a #hash-target on page load / transition because the element hasn't rendered yet. There is an issue about this for Ember here on the RFCs repo.

Solution

ember-url-hash-polyfill

The addon ember-url-hash-polyfill provides a way to support the behaviour that is in normally native to browsers where an anchor tag with href="#some-id-or-name" would scroll down the page when clicked.

The problem with ember-url-hash-polyfill solution is that it skips the use of <LinkTo> in favour of native HTML <a> tag. If you can't/don't want do that, the next section is for you.

ember-scroll-modifiers

The ember-scroll-modifiers addon offers scroll-into-view modifier which can be used here. Drawback being that we won't be using hash property of the URL, but rather query-param.

First we need to define our query param which will be named scrollTo and will live on application controller, so that we have access to it from every route:

// controllers/application.ts
import { service } from '@ember/service';
import type ScrollService from 'my-app/services/scroll';

export default class ApplicationController extends Controller {
  @service declare scroll: ScrollService;

  queryParams = ['scrollTo'];
}
Enter fullscreen mode Exit fullscreen mode

Then we will create scroll service which will be able to tell us which scrollTo was activated:

// services/scroll.ts

export default class ScrollService extends Service {
  @service declare router: RouterService;

  get inView() {
    return this.router.currentRoute.queryParams['scrollTo'];
  }

  get heroInView() { // cheap example, add yours to expand
    return this.inView === 'hero';
  }
}
Enter fullscreen mode Exit fullscreen mode

And finally and example of template that defines a link with scrollTo query parameter. And an element that we wish to scroll to marked with scroll-int-view modifier.

// templates/application.hbs

<LinkTo
  @route="index"
  @query={{hash scrollTo="hero"}}
>
  Link to Hero
</LinkTo>

...

<div
  {{scroll-into-view
    shouldScroll=this.scroll.heroInView
    options=(hash behavior="smooth")
  }}
>
  I am hero!
</div>
Enter fullscreen mode Exit fullscreen mode

Nice thing is that we can also invoke route transition & scrollTo programatically via router service transitionTo:

this.router.transitionTo('index', {
  queryParams: { scrollTo: 'hero' },
});
Enter fullscreen mode Exit fullscreen mode

Cover image generated via Midjourney prompt: scrolling in web browser --ar 16:9

Top comments (0)