VueJS scroll to section from different route - vue.js

I am trying to scroll to an anchor on a page using Vue and Vue Router (with history mode).
When on the index page, the scroll behaviour works as expected by jumping to the section.
However, when I am another page, it loads the index page at the top and not where the anchor is pointing to.
I’m sure it’s a very simple thing but can’t get my head round it!
Any help is appreciated!
My router index:
export default new Router({
scrollBehavior: function(to, from, savedPosition) {
if (to.hash) {
return {selector: to.hash}
} else {
return {x: 0, y: 0}
}
},
mode: 'history',
routes: [ ... ]
})
My Navigation:
<router-link #click.native="closeNav" to="/#enter">Enter</router-link>
<router-link #click.native="closeNav" to="/#prizes">Prizes</router-link>
<router-link #click.native="closeNav" to="/#faqs">FAQ</router-link>
<router-link #click.native="closeNav" to="/contactus">Contact</router-link>

Vue Router v3.x
This is a bit of an old question and OP has almost surely found a solution already, but for anyone running into this problem, this should do the trick:
<router-link :to="{ name: 'Homepage', hash: '#enter' }">Enter</router-link>
<router-link :to="{ name: 'Homepage', hash: '#prizes' }">Prizes</router-link>
<router-link :to="{ name: 'Homepage', hash: '#faqs' }">FAQ</router-link>
<router-link :to="{ name: 'Contact' }">Contact</router-link>
This should allow you to have these links accessible from other views/components, and when clicked will redirect you to the named route (Homepage in this case), and scroll to the hash specified (#enter, #prizes, #faqs).
In addition to the router code snippet in the question, you can add smooth scrolling using the native window.scrollTo method:
export default new Router({
routes: [],
mode: 'history',
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return window.scrollTo({
top: document.querySelector(to.hash).offsetTop,
behavior: 'smooth'
})
} else {
return { x: 0, y: 0 }
}
}
})
Update for Vue Router v4.x
You write your router-links the same, but you can write the scroll behavior and element selection a bit neater now. From the docs
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
if (to.hash) {
return {
el: to.hash,
behavior: 'smooth'
}
}
}
})

I am using scrollIntoView() instead of window.scrollTo()
export default new Router({
routes: [],
mode: 'history',
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return document.querySelector(to.hash).scrollIntoView({ behavior: 'smooth' });
} else {
return savedPosition || { x: 0, y: 0 }
}
}
})

Related

Vue router - path #ID scrollBehavior

I have created a MENU where I link via <router-link> but certain links are linked to the same page using (anchors).
When I'm on the Work page and I click on the #services section, which is on the Bio page, the section is displayed correctly, but if I want to go to the services section on the Bio page, the URL just changes, but it won't go to the right section for me.
noubtest.com
NAVIGATION
<router-link v-show="!mobile" class="link bio" :to="{ name: 'Home' }">Bio</router-link>
<router-link v-show="!mobile" class="link link2" :to="{ name: 'Services' }">Services</router-link>
<router-link v-show="!mobile" class="link link2" :to="{ name: 'SelectedWork' }">Work</router-link>
ROUTER
{
path: "/home",
name: "Home",
component: Home,
meta: {
title: "Bio",
requiresAuth: false,
},
},
{
path: "/home#fifthPage",
name: "Services",
component: Home,
meta: {
title: "Services",
requiresAuth: false,
},
},
const router = new VueRouter({
mode: "history",
routes,
scrollBehavior() {
return { x: 0, y: 0 };
},
});
router.beforeEach((to, from, next) => {
document.title = `${to.meta.title} | YounesFilm`;
next();
});
router.beforeEach(async (to, from, next) => {
let user = firebase.auth().currentUser;
let admin = null;
if (user) {
let token = await user.getIdTokenResult();
admin = token.claims.admin;
}
if (to.matched.some((res) => res.meta.requiresAuth)) {
if (user) {
if (to.matched.some((res) => res.meta.requiresAdmin)) {
if (admin) {
return next();
}
return next({ name: "Home" });
}
return next();
}
return next({ name: "Home" });
}
return next();
});
export default router;
How can I click through the page between sections?
You must switch your VueRouter from hash mode to history mode of routing - then hashtags will work but in a different way.
Your routes should not have a hash symbol # inside their path - instead, you should provide it under the hash attribute of the route link:
<router-link :to="{ name: pathName, hash: '#text' }">
Jump to content
</router-link>
But this alone is not enough. You also need to alter the scrollBehavior of the VueRouter:
import { routes } from './routes.js';
const router = new VueRouter({
routes,
scrollBehavior(to, from, savedPosition)
{
if (savedPosition)
{
return savedPosition;
}
if (to.hash)
{
return { selector: to.hash }; // <==== the important part
}
return { x: 0, y: 0 };
}
});
With a few research, I found two things that could help you.
First, this error is known and discussed on github vue-router issues page.
Second, I found that Workaround on npmjs.com, and you could probably give it a try.
EDIT
I found another solution to a similar problem here.
And from that page, I found a scrollBehavior example like this:
scrollBehavior: function (to) {
if (to.hash) {
return {
selector: to.hash
}
}
}
And if it still doesn't work, you could try to use
:to="{ name: 'Home', hash: 'fifthPage'}".

Vue router not scrolling when i set hash property

const router = new VueRouter ({
mode: 'history',
routes,
scrollBehavior(to,from, savedPosition) {
if(to.hash) {
return {
selector: to.hash,
behavior: "smooth"
}
}
console.log(from, savedPosition)
console.log(to)
}
});
In my Home component :
<router-link :to="{path: '/about', hash: '#test'}" tag='a' class="green--cta welcome__cta">дознај повеќе</router-link>
First when i click button it sets the location to /about#test but doesn't scroll to the element with that id.I tried to log it into console but when i add hash nothing shows.When i refresh the page it scroll to the element with that id.

How can vue-router push({name:"question"}) with hash?

eg: the changed route is https://stackoverflow.com/question#hello
router.push(location, onComplete?, onAbort?)
name is required in localtion
Vue Router allows you to completely customize the scroll behavior on route navigation. Vue scroll behavior is a wide topic, so you can dive into docs
For your example I think you need hash prop, with scroll behavior:
Router.push({ name: routeName, hash: '#toHash' })
router.push({ name: 'question', hash: '#hello' }) can work
For Router.push({ name: routeName, hash: '#toHash' }) to work, you need to configure your vue router.
// router.js file
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
// Your Routes
],
// Ref: https://router.vuejs.org/guide/advanced/scroll-behavior.html
scrollBehavior(to, from, savedPosition) {
if (to.hash) {
// This ensures that if hash is provided to router.push it works as expected.
// & since we have used "behavior: 'smooth'" the browser will slowly come to this hash position.
return {
el: to.hash,
behavior: 'smooth',
}
}
}
});
This code assumes you are using vue-router v4.

Vue.js scroll to top of page for same route

I can set scrolling behaviour to Vue.js Router like this:
const router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'index',
component: Main
},
{
path: '/some-path',
name: 'some-path',
component: SomePath
}
],
scrollBehavior() {
return {x: 0, y: 0}
}
})
This works perfectly when you click on the link with some page which is not current. When I click on the link which is already rendered, i.e. in the footer, nothing happens. Vue Router assumes there is no state transition. What is the preferred way to scroll up in this case?
You can't do this through vue-router, but you can add a scroll-to-top method to every router-link.
Just create a method like this:
methods: {
scrollToTop() {
window.scrollTo(0,0);
}
}
Add it to the link:
<router-link #click.native="$scrollToTop">
If you want to use it outside of your footer too, it's better to add it to the Vue prototype
Vue.prototype.$scrollToTop = () => window.scrollTo(0,0)
It's not a 100% solution but it's the simplest one
I couldn't get any of the above solutions working, and it was really frustrating.
What ended up working for me was the below:
const router = new Router({
mode: 'history',
routes: [...],
scrollBehavior() {
document.getElementById('app').scrollIntoView({ behavior: 'smooth' });
}
})
I mount my VueJs app to #app so I can be certain it is present and is available for selection.
You could make use of behavior: smooth:
moveTo () {
let to = this.moveToDown
? this.$refs.description.offsetTop - 60
: 0
window.scroll({
top: to,
left: 0,
behavior: 'smooth'
})
this.moveToDown = !this.moveToDown
}
The best solution I've found for this is: https://router.vuejs.org/guide/advanced/scroll-behavior.html
Specifically:
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
return { x: 0, y: 0 }
}
})
Expanding on the answer from Vitaly Migunov, you can instead add directly from the router a scrollTo method to the window object. This way you won't need to add the function to every router link.
const router = new Router({
mode: 'history',
routes: [...],
scrollBehavior() {
window.scrollTo(0,0);
}
})
Use refs for scroll to certain section
<template>
<body>
<div ref="section">
// Your content section
</div>
</body>
</template>
export default class MyPage extends Vue {
$refs!: {
section: HTMLFormElement;
};
scrollToTop() {
this.$refs.section.scrollTo(0, 0);
}
}
This worked for me:
const router = createRouter({
history: createWebHashHistory(),
routes,
scrollBehavior() {
document.getElementById('app').scrollIntoView({behavior:'smooth'});
}
})
Basically, I think you want to scroll to the top of the page, unless an internal page location is specified (e.g. www.example.com/home#blah). All of the answers so far would ignore this location parameter, and just scroll to the top anyway.
scrollBehavior(to, from, savedPosition) {
//If there is no hash parameter when we change vue, scroll to top of page
if (!to.hash) {
return { x: 0, y: 0 }
}
}
We can check if there is a location parameter using to.hash, then only scroll to the top if no hash location is present.
for vue3
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
scrollBehavior(to, from, savedPosition) {
// always scroll to top
return { top: 0 }
},
})
For Vue3 you should use scrollBehavior. Use left and top instead of x and y.
scrollBehavior(to, from, savedPosition) {
return { left: 0, top: 0, behavior: "smooth" };
}
I've tried all of the above answers and none did work for me; However I've tried this one and it did the job for me; Add this to your App.vue file
updated() {
this.$refs.main.scrollTo(0, 0)
},
Vue Js have inbuilt support for scrolling if the browser supports history.pushState.
It is very easy to configure, Just provide the scrollBehavior function, when creating Vue router instance like below:
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// page scroll to top for all route navigations
return { x: 0, y: 0 }
}
})
For more options and detail about Vue Scroll Behavior click here
Use this nice component: https://www.npmjs.com/package/vue-backtotop
<back-to-top text="Back to top"></back-to-top>
Alternatively, this also worked for me:
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
scrollBehavior(to, from, savedPosition) {
document.getElementById('app').scrollTop = 0
},
routes
})

Vue.js scroll to top of new page route after setTimeout

I have a page transition that doesn't work nicely when the scroll to the top of a new route is instant. I'd like to wait 100ms before it automatically scrolls to the top. The following code doesn't end up scrolling at all. Is there a way to do this?
export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'Home',
component: Home
}
],
scrollBehavior (to, from, savedPosition) {
setTimeout(() => {
return { x: 0, y: 0 }
}, 100);
}
})
This is natively supported by Vue now, use scrollBehaviour, like this:
export default new Router({
scrollBehavior() {
return { x: 0, y: 0 };
},
routes: [
{
path: '/',
name: 'Home',
component: Home
}
],
mode: 'history'
});
More here.
The other answers fail to handle edge cases such as:
Saved
Position - The saved position occurs when the user clicks the back or forward positions. We want to maintain the location the user was looking at.
Hash Links - E.g. http://example.com/foo#bar should navigate to the element on the page with an id of bar.
Finally, in all other cases we can navigate to the top of the page.
Here is the sample code that handles all of the above:
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes,
scrollBehavior: (to, from, savedPosition) => {
if (savedPosition) {
return savedPosition;
} else if (to.hash) {
return {
selector: to.hash
};
} else {
return { x: 0, y: 0 };
}
}
});
If you want this to happen on every route, you can do so in the before hook in the router:
const router = new VueRouter({ ... })
router.beforeEach(function (to, from, next) {
setTimeout(() => {
window.scrollTo(0, 0);
}, 100);
next();
});
If you are on an older version of vue-router, use:
router.beforeEach(function (transition) {
setTimeout(() => {
window.scrollTo(0, 0);
}, 100);
transition.next();
});
If you want to wait a long time use Async Scrolling of scrollBehaviour, like this:
export default new Router({
scrollBehavior() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ x: 0, y: 0 })
}, 100)
})
},
routes: [
{
path: '/',
name: 'Home',
component: Home
}
],
mode: 'history'
});
More here.
This is probably not the best way, but adding
document.body.scrollTop = document.documentElement.scrollTop = 0;
in a route's core component's (in this case, Home) mounted() function achieves what I want.
When using client-side routing, we may want to scroll to top when navigating to a new route, or preserve the scrolling position of history entries just like real page reload does. vue-router allows you to achieve these and even better, allows you to completely customize the scroll behavior on route navigation.
Note: this feature only works if the browser supports history.pushState.
scrollBehavior (to, from, savedPosition) {
return { x: 0, y: 0 }
}
With Saved Position:
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
For more information