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'];
}
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';
}
}
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>
Nice thing is that we can also invoke route transition & scrollTo programatically via router service transitionTo:
this.router.transitionTo('index', {
queryParams: { scrollTo: 'hero' },
});
Cover image generated via Midjourney prompt: scrolling in web browser --ar 16:9
Top comments (0)