Closed ParachuteCat closed 3 years ago
I made a workaround by using the method $router.push
instead of the router-link
component.
This method takes a success callback (in this case irrelevant) as 2nd and a abort callback as 3rd parameter.
$router.push(route, success, abort)
These callbacks will be called when the navigation either successfully completed (after all async hooks are resolved), or aborted (navigated to the same route, or to a different route before current navigation has finished), respectively.
@ParachuteKadse Hey mate, I came across the same problem, have you solved this one? If so, would you mind sharing your solution here?
Cheers
savedPosition, is only available if this is a popstate navigation (triggered by the browser's back/forward buttons). The object could be in the form of:
If a falsy value or an empty object is returned, no scrolling will happen. read more
he popstate event is only triggered by performing a browser action, such as clicking on the back button (or calling history.back() in JavaScript)... read more
savedPosition is meant for save the scroll position object when using back in browser. (AFAIK)
official example also suggests use hash for anchor behavior
https://github.com/vuejs/vue-router/blob/dev/examples/scroll-behavior/app.js
Your expected behavior can be achieved by vue-scroll plugins
or use standalone solution using #ids with libraries like jump, zenscroll or other similar libraries
Possible workaround:
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')
I think it would be fantastic to improve the anchor support. I ran into quite a few questions and issues around this. After reading what everyone has written above I thought I'd paste my solution. I created an anchor-link
component composed of a router-link
that is used in conjunction with the scrollBehavior
function. I haven't thoroughly tested it but here is the basic idea (I think this is the same route check could be improved):
anchor-link.vue
<template>
<span @click="navigate"><router-link ref="link" :to="to"><slot></slot></router-link></span>
</template>
<script>
export default {
name: 'anchor-link',
props: ['to'],
methods: {
navigate() {
const current = this.$router.currentRoute.fullPath;
const href = this.$refs.link.$el.getAttribute('href');
if (current === href) {
location.href = this.to.hash;
}
}
}
};
</script>
router.js excerpt
function scrollBehavior(to, from, savedPosition) {
if (to.hash && document.querySelector(to.hash)) {
return { selector: to.hash };
}
if (savedPosition) {
return savedPosition;
}
return false;
}
const router = new Router({ mode: 'history', routes, scrollBehavior });
None of these solutions really work on reloading the page I tried stuff like below but there obviously a race condition, it doesn't really work. Not sure how to come up with a good solution for anchor pages on reload. I feel like we might be competing with the browser's default behaviour. Maybe someone with more experience could lend some insight?
mounted() {
const anchor = this.$router.currentRoute.hash;
this.$nextTick(() => {
if (anchor && document.querySelector(anchor)) {
location.href = anchor;
}
});
}
I gave up on hacks and workarounds. I've tracked this issue down to lines 1916-1923 in vue-router.js
. Commenting the if statement out seems to work, though I'm sure it would be better if it were updated to support same hash navigation.
if (
isSameRoute(route, current) &&
// in the case the route map has been dynamically appended to
route.matched.length === current.matched.length
) {
this.ensureURL();
return abort()
}
The @aldencolerain 's implementation worked perfectly for me
mounted() {
const anchor = this.$router.currentRoute.hash;
this.$nextTick(() => {
if (anchor && document.querySelector(anchor)) {
location.href = anchor;
}
});
}
I went with @gspain solution. I didn't noticed any implications yet (few weeks).
This worked for me: My Vue Router uses the default mode (hash). The way I see it, because my URLs have 2 hash symbols, the browser behavior of scrolling to a #some-id
at the end of the URL breaks. I used code from earlier in this thread to make my own fix that only restores the scrolling feature, instead of changing the history stack. Also, my default scroll behavior is to always begin at the very top.
This works whether you inter-site link, navigate directly using the address bar, or refresh the page.
router/index.js
const router = new Router({
routes: [
// route objects here
// ...
],
scrollBehavior(to, from, savedPosition) {
return {
x: 0,
y: 0,
};
},
});
main.js
import Vue from 'vue';
import App from './App';
import router from './router';
Vue.config.productionTip = false;
const fixIdScrolling = {
watch: {
$route(to, from) {
const currentRoute = this.$router.currentRoute;
const idToScrollTo = currentRoute.hash;
this.$nextTick(() => {
if (idToScrollTo && document.querySelector(idToScrollTo)) {
document.querySelector(idToScrollTo).scrollIntoView();
}
});
},
},
};
/* eslint-disable no-new */
new Vue({
mixins: [fixIdScrolling],
el: '#app',
router,
components: {App},
template: '<App/>',
});
This worked for me: My Vue Router uses the default mode (hash). The way I see it, because my URLs have 2 hash symbols, the browser behavior of scrolling to a
#some-id
at the end of the URL breaks. I used code from earlier in this thread to make my own fix that only restores the scrolling feature, instead of changing the history stack. Also, my default scroll behavior is to always begin at the very top.This works whether you inter-site link, navigate directly using the address bar, or refresh the page.
router/index.js
const router = new Router({ routes: [ // route objects here // ... ], scrollBehavior(to, from, savedPosition) { return { x: 0, y: 0, }; }, });
main.js
import Vue from 'vue'; import App from './App'; import router from './router'; Vue.config.productionTip = false; const fixIdScrolling = { watch: { $route(to, from) { const currentRoute = this.$router.currentRoute; const idToScrollTo = currentRoute.hash; this.$nextTick(() => { if (idToScrollTo && document.querySelector(idToScrollTo)) { document.querySelector(idToScrollTo).scrollIntoView(); } }); }, }, }; /* eslint-disable no-new */ new Vue({ mixins: [fixIdScrolling], el: '#app', router, components: {App}, template: '<App/>', });
Thanks heaps this should be implemented
The changes from @ajb413 mostly work, except when reloading the page or navigating from an external site. Anyone else having those issues?
@pkkid Having the same issue. Both scrollBehavior
and fixIdScrolling
only trigger when the app is changing
a route not when the Vue application is first entered nor when the route remains the same.
Personally I'd love to see some functionality that allows me to override that behavior.
The @aldencolerain 's implementation worked perfectly for me
mounted() { const anchor = this.$router.currentRoute.hash; this.$nextTick(() => { if (anchor && document.querySelector(anchor)) { location.href = anchor; } }); }
This work with Firefox and mobile(chrome) but does not work with desktop(chrome). chrome(desktop) save the last scroll position and scroll to it after refresh . . Edit: I add history.scrollRestoration = "manual"; inside Then it work with chrome(desktop)
This is by far the cleanest solution that i have found and it works perfectly.
methods: {
goto(refName) {
var element = this.$refs[refName];
element.scrollIntoView();
}
}
If you wanted to get fancy and make the scroll smooth, you can even add the following:
element.scrollIntoView({ behavior: 'smooth' });
@gspain @SilverDragon135 Is your solution still working for you? It seems that something has changed since your post. At least it's not working for me. Same behavior.
It's been almost 4 years. How has this not been fixed yet???
My grandfather had said that one day this bug would be fixed but no one believed him. Here we are after ages...
Already 2023, Any further progress??
Version
2.4.2
Reproduction link
https://jsfiddle.net/ParachuteKadse/pL0sehq5/show/
Steps to reproduce
What is expected?
Scroll down to Foo-3, like the first time.
What is actually happening?
Screen stays at the same position and doesn't scroll to Foo-3.
Special notes
You can see the correct URL in the status-bar of your browser (Tested on Chrome)