I'm looking for a smart way to handle in-page anchors with Vue Router. Consider the following:
<router-link to="#app">Apply Now</router-link> <!-- some HTML markup in between... --> <div id="app">...</div>
The "scroll to anchor" behavior described in the docs works fine except:
div id="app"
. Now scroll away from the div
back to the anchor and try clicking it again -- this time you will not jump down to the div
. In fact, the anchor will retain the class router-link-active
and the URL will still contain the hash /#app
;This is very unfortunate from the UX perspective because a potential customer has to manually scroll all the way down again to reach the application section.
I was wondering if Vue Router covers this situation. For reference, here's my router:
export default new VueRouter({ routes, mode: 'history', scrollBehavior(to, from, savedPosition) { if (to.hash) { return { selector: to.hash } } else if (savedPosition) { return savedPosition; } else { return { x: 0, y: 0 } } } })
Vue Router 4 provides multiple ways of doing this: from setting the props property on the route record to true and automatically passing all params as props, setting the props property to a static object to provide static props, or setting it to a function that returns the desired props.
You can't use . vue files using the CDN version of Vue and Vue Router because the . vue filetype is part of the vue-loader project for Webpack. In other words, you need to transition over to using Webpack if you wanna use .
We map the URL paths to components. Then we call createRouter with an object that has the history property set to value returned by createWebHistory . createWebHistory set the router to HTML5 history mode to remove the hash. This will remove the hash from the paths that are mapped by Vue Router 4.
I haven't found anything in the resources to solve your issue but you could utitlize the $route.hash
in your mounted
hook of the component that holds your <router-view></router-view>
to solve the refresh issue.
<script> export default { name: 'app', mounted: function() { // From testing, without a brief timeout, it won't work. setTimeout(() => this.scrollFix(this.$route.hash), 1); }, methods: { scrollFix: function(hashbang) { location.hash = hashbang; } } } </script>
Then to solve the issue of second clicks you could use the native
modifier and bind to your <router-link></router-link>
. It's a fairly manual process but will work.
<router-link to="#scroll" @click.native="scrollFix('#scroll')">Scroll</router-link>
There may also be something you could do with the router's afterEach
method but haven't figured that out yet.
Possible solution which is more resusable IMO:
this.$router.push({ name: 'home' }, undefined, () => { location.href = this.$route.hash })
As the 3rd argument is the abort() function, it may have unwanted side effects though..
If you want to use it globally, add a function to your Router:
pushWithAnchor: function (routeName, toHash) { const fromHash = Router.history.current.hash fromHash !== toHash || !fromHash ? Router.push({ name: routeName, hash: toHash }) : Router.push({ name: routeName, hash: fromHash }, undefined, () => { window.location.href = toHash }) }
And use it in components with:
this.$router.options.pushWithAnchor('home', '#fee-calculator-section')
Within a template you could do something like:
<a @click="this.$router.options.pushWithAnchor('home', '#fee-calculator-section')"></a>
Sadly you cant use a scroll offset though
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With