How to create anchor tags with Vue Router
I am creating a small Vue webapp, I want to create an anchor tag in this.
I have given an id
to one of the div
I wanted to have anchor tags like this:
<div id="for-home">
....
</div>
And here is my router configuration:
export default new Router({
abstract: true,
mode: 'history',
scrollBehavior: () => ({ y: 0 }),
routes: [
{ path: "https://stackoverflow.com/", component: abcView}
]
})
But with this anchor tags are sometimes working and sometimes not working, Have I missed something in this?
I believe you are asking about jumping directly to a particular area of page, by navigating to an anchor tag like #section-3
within the page.
For this to work, you need to modify your scrollBehavior function as follows:
new VueRouter({
mode: 'history',
scrollBehavior: function(to, from, savedPosition) {
if (to.hash) {
return {selector: to.hash}
//Or for Vue 3:
//return {el: to.hash}
} else {
return { x: 0, y: 0 }
}
},
routes: [
{path: "https://stackoverflow.com/", component: abcView},
// your routes
]
});
Ref: https://router.vuejs.org/guide/advanced/scroll-behavior.html#async-scrolling
I attempted creating a jsFiddle example but it is not working because of mode:'history'
. If you copy the code and run locally, you will see how it works: https://jsfiddle.net/mani04/gucLhzaL/
By returning {selector: to.hash}
(or {el: to.hash}
in Vue 3) in scrollBehavior, you will pass the anchor tag hash to the next route, which will navigate to the relevant section (marked using id
) in that route.
For me the {selector: to.hash}
solution just refused to work with vue-router 4.0.0. The following approach worked and also enabled smooth scrolling.
The timeout of 500ms is there to allow the page to load, because I found that otherwise smooth scrolling would not scroll to the correct position (because the page was still loading).
const scrollBehavior = (to, from, savedPosition) => {
if (to.hash) {
setTimeout(() => {
const element = document.getElementById(to.hash.replace(/#/, ''))
if (element && element.scrollIntoView) {
element.scrollIntoView({block: 'end', behavior: 'smooth'})
}
}, 500)
//NOTE: This doesn't work for Vue 3
//return {selector: to.hash}
//This does
return {el: to.hash};
}
else if (savedPosition) {
return savedPosition
}
return {top: 0}
}
I have just came across this problem too and I thought there is a little space to optimize the page swap. In my case I’d like to make a smooth transition instead of “jumping arround”. I’ve already required in jQuery as alias for $.
Here is my Router Setup for the smooth animation, feel free to modify the lines to your needs accordingly. This code could been made cleaner but should be fine enough to show you the working idea.
// Register Router and Paths
const router = new VueRouter({
mode: 'history',
routes : [
{ path: '/aboutme/', component: index, name:'me.index'},
{ path: '/aboutme/cv', component: cv, name:'me.cv' }
],
// Build smooth transition logic with $
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return $('html,body').stop().animate({scrollTop: $(to.hash).offset().top }, 1000);
} else {
return $('html,body').stop().animate({scrollTop: 0 }, 500);
}
}
})
Side Question on my side: Do we have to return anything? I don’t have any trouble with or without returning due the jQuery Animation is handling the page scroll.
regards Gkiokan
If you need to make animated scrollTo I use package vue-scrollTo: it’s very easy to setup.
Examples with docs and code can be found here: https://github.com/rigor789/vue-scrollto/
I have tried Adam Reis’s answer, it works. But I feel it’s kinda not that good compare to the original return {selector: to.hash}
when you refresh the page. So I tried a different approach by using promise. It works the same way as the original.
Here is the code:
function scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
if (to.hash) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ selector: to.hash })
}, 300)
})
}
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ x: 0, y: 0 })
}, 300)
})
}
I think the problem is the hash route executes before the page is fully loaded. That is why It does nothing because no element with the id show up yet. But I don’t know why if I refresh the page, It works great. Anyway, this is my solution. I hope it helps any newcomer.
Note:
- This code works well on navigating to a different page or the same page. Adjust the timeout as you need.
- For smooth navigation, I use CSS only. On the root element/ element That has the scroll, you can add
scroll-behavior: smooth;
to make it smooth, as mentioned on MDN. - I used vue-router 3.5.1 as I still use Vue 2.x. If you using Vue 3, you may adjust the code as rits said in Adam Reis’s answer.