When clicking a link on the app, the Router does not scroll to the correct hash location given. But it DOES scoll. The scroll is always about 1 element too high on the page. For example, if my page is this:
<template>
<div id="div1">Div1</div>
<div id="div2">Div2</div>
<div id="div3">Div3</div>
<div id="div4">Div4</div>
</template>
Then if I click a link that goes to hash "div3" it will scroll me so the top of div2. Only a very few select elements are scrolled to correctly. Is there a reason for this? Setting page margins?
Router code:
const router = new VueRouter({
routes,
mode: 'history',
base: "/",
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return {
selector: to.hash,
behavior: 'smooth',
}
} else {
return { x: 0, y: 0 }
}
}
})
Example of code that calls the hash routing:
if (item.title == "Mission") {
router.push({name: 'Explore', hash: '#mission-statement'});
} else if (item.title == "Our Story") {
router.push({name: 'Explore', hash: '#our-story-container'});
} else if (item.title == "Shared principles") {
router.push({name: 'Explore', hash: '#shared-principles-container'});
} else if (item.title == "Volunteer Opportunities") {
router.push({name: 'Explore', hash: '#volunteer-container'});
} else if (item.title == "Gallery") {
router.push({name: 'Explore', hash: '#galleries'});
} else if (item.title == "Living") {
router.push({name: 'Explore', hash: '#living-container'});
} else if (item.title == "Contact Us") {
router.push({name: 'Explore', hash: '#contact-us-container'});
} else {
router.push("/explore")
}
SOLUTION:
With the help of IVO GELOV's answer I was able to update the scroll behavior function with the code below and all works as expected.
scrollBehavior (to, from, savedPosition) {
let position = {}
if (to.hash) {
position = {
selector: to.hash,
offset: { x: 0, y: 100 },
behavior: 'smooth',
}
} else {
position = { x: 0, y: 0 }
}
return new Promise((resolve) => {
setTimeout(() => {
resolve(position)
}, 100)
})
}
CodePudding user response:
Nuxt.js is using the following code in its ./nuxt/router.js
- perhaps you can adopt it to your usecase:
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
if (process.client) {
window.history.scrollRestoration = 'manual'
}
const scrollBehavior = function (to, from, savedPosition) {
// if the returned position is falsy or an empty object,
// will retain current scroll position.
let position = false
// if no children detected
if (to.matched.length < 2) {
// scroll to the top of the page
position = { x: 0, y: 0 }
} else if (to.matched.some((r) => r.components.default.options.scrollToTop)) {
// if one of the children has scrollToTop option set to true
position = { x: 0, y: 0 }
}
// savedPosition is only available for popstate navigations (back button)
if (savedPosition) {
position = savedPosition
}
return new Promise(resolve => {
// wait for the out transition to complete (if necessary)
window.$nuxt.$once('triggerScroll', () => {
// coords will be used if no selector is provided,
// or if the selector didn't match any element.
if (to.hash) {
let hash = to.hash
// CSS.escape() is not supported with IE and Edge.
if (typeof window.CSS !== 'undefined' && typeof window.CSS.escape !== 'undefined') {
hash = '#' window.CSS.escape(hash.substr(1))
}
try {
if (document.querySelector(hash)) {
// scroll to anchor by returning the selector
position = { selector: hash }
}
} catch (e) {
console.warn('Failed to save scroll position. Please add CSS.escape() polyfill (https://github.com/mathiasbynens/CSS.escape).')
}
}
resolve(position)
})
})
}
export function createRouter () {
return new Router({
mode: 'history',
base: '/',
linkActiveClass: 'nuxt-link-active',
linkExactActiveClass: 'nuxt-link-exact-active',
scrollBehavior,
routes: [ .... ],
fallback: false
})
}
An example implementation (taken from https://dev.to/dimer191996/nuxt-js-smooth-scrolling-with-hash-links-94a) - you may also need to set window.history.scrollRestoration = 'manual'
if your <router-view>
is wrapped inside a <transition>
:
scrollBehavior: async (to, from, savedPosition) => {
if (savedPosition) {
return savedPosition
}
const findEl = async (hash, x) => {
return document.querySelector(hash) ||
new Promise((resolve, reject) => {
if (x > 50) {
return resolve()
}
setTimeout(() => { resolve(findEl(hash, x || 1)) }, 100)
})
}
if (to.hash) {
let el = await findEl(to.hash)
if ('scrollBehavior' in document.documentElement.style) {
return window.scrollTo({ top: el.offsetTop, behavior: 'smooth' })
} else {
return window.scrollTo(0, el.offsetTop)
}
}
return { x: 0, y: 0 }
}