Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Scroll to anchor not working #213 #214

Merged
merged 6 commits into from
May 29, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion packages/saber/vue-renderer/app/create-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ClientOnly from './components/ClientOnly'
import extendBrowserApi from '#cache/extend-browser-api'
import injectConfig from './helpers/inject-config'
import setTransition from './helpers/set-transition'
import scrollHandler from './helpers/scroll-handler'

Vue.config.productionTip = false

Expand Down Expand Up @@ -74,6 +75,15 @@ export default context => {
transition: null
}
},
mounted() {
this.$router.app.$nextTick(() =>
krmax44 marked this conversation as resolved.
Show resolved Hide resolved
scrollHandler(
this.$router,
this.$router.currentRoute,
this.$router.currentRoute
)
)
},
render(h) {
const transition = Object.assign({}, this.transition)
const listeners = {}
Expand All @@ -89,7 +99,9 @@ export default context => {
})
const beforeEnter = listeners['before-enter']
listeners['before-enter'] = el => {
this.$emit('trigger-scroll')
this.$nextTick(() => {
this.$emit('trigger-scroll')
})
beforeEnter && beforeEnter(el)
}
const children = [
Expand Down
100 changes: 100 additions & 0 deletions packages/saber/vue-renderer/app/helpers/scroll-handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
modified after Vue Router's scroll util
https://github.com/vuejs/vue-router/blob/3c7d8ab20f9c716652e92065767a5a44ffb21c13/src/util/scroll.js
*/

/**
*
* @param {object} router Vue Router instance
* @param {object} to Destination route
* @param {object} from Starting route
*/
export default function(router, to, from) {
if (!router.app) {
return
}

const behavior = router.options.scrollBehavior
if (!behavior) {
return
}

// wait until re-render finishes before scrolling
router.app.$nextTick(() => {
const shouldScroll = behavior.call(router, to, from, null)

if (!shouldScroll) {
return
}

if (typeof shouldScroll.then === 'function') {
shouldScroll
.then(shouldScroll => {
scrollToPosition(shouldScroll)
})
.catch(err => {
if (process.env.NODE_ENV !== 'production') {
console.error(err)
}
})
} else {
scrollToPosition(shouldScroll)
}
})
}

function getElementPosition(el, offset) {
const docEl = document.documentElement
const docRect = docEl.getBoundingClientRect()
const elRect = el.getBoundingClientRect()
return {
x: elRect.left - docRect.left - offset.x,
y: elRect.top - docRect.top - offset.y
}
}

function isValidPosition(obj) {
return isNumber(obj.x) || isNumber(obj.y)
}

function normalizePosition(obj) {
return {
x: isNumber(obj.x) ? obj.x : window.pageXOffset,
y: isNumber(obj.y) ? obj.y : window.pageYOffset
}
}

function normalizeOffset(obj) {
return {
x: isNumber(obj.x) ? obj.x : 0,
y: isNumber(obj.y) ? obj.y : 0
}
}

function isNumber(v) {
return typeof v === 'number'
nblthree marked this conversation as resolved.
Show resolved Hide resolved
}

function scrollToPosition(shouldScroll) {
let position
const isObject = typeof shouldScroll === 'object'
if (isObject && typeof shouldScroll.selector === 'string') {
const el = document.querySelector(shouldScroll.selector)
if (el) {
let offset =
shouldScroll.offset && typeof shouldScroll.offset === 'object'
? shouldScroll.offset
: {}
offset = normalizeOffset(offset)
position = getElementPosition(el, offset)
} else if (isValidPosition(shouldScroll)) {
position = normalizePosition(shouldScroll)
}
} else if (isObject && isValidPosition(shouldScroll)) {
position = normalizePosition(shouldScroll)
}

if (position) {
window.scrollTo(position.x, position.y)
}
}
100 changes: 46 additions & 54 deletions packages/saber/vue-renderer/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,65 +31,57 @@ if (process.client) {
}

export default () => {
const createRouter = routes => new Router({
mode: 'history',
routes,
scrollBehavior(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 and scrollToTop is not explicitly disabled
if (
to.matched.length < 2 &&
to.matched.every(
r => r.components.default.scrollToTop !== false
)
) {
// scroll to the top of the page
position = { x: 0, y: 0 }
} else if (
to.matched.some(r => r.components.default.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)
router.app.$once('trigger-scroll', () => {
// 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)) {
const createRouter = routes =>
new Router({
mode: 'history',
routes,
scrollBehavior(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 and scrollToTop is not explicitly disabled
if (
to.matched.length < 2 &&
to.matched.every(r => r.components.default.scrollToTop !== false)
) {
// scroll to the top of the page
position = { x: 0, y: 0 }
} else if (to.matched.some(r => r.components.default.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 => {
const fulfill = () => {
// coords will be used if no selector is provided,
// or if the selector didn't match any element.
if (to.hash) {
if (document.getElementById(to.hash.substr(1))) {
// scroll to anchor by returning the selector
position = { selector: hash }
position = { selector: to.hash }
} else {
// scroll to top if anchor does not exist and position is not already set
position = position || { x: 0, y: 0 }
}
} catch (e) {
console.warn(
'Failed to save scroll position. Please add CSS.escape() polyfill (https://github.com/mathiasbynens/CSS.escape).'
)
}
resolve(position)
}

// wait for the out transition to complete (if necessary)
if (to.path === from.path) {
fulfill()
} else {
router.app.$once('trigger-scroll', fulfill)
}
resolve(position)
})
})
}
})
}
})

const router = createRouter(routes)

Expand Down
1 change: 0 additions & 1 deletion website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
},
"dependencies": {
"date-fns": "1.30.1",
"jump.js": "1.0.2",
"markdown-it-footnote": "3.0.1",
"nprogress": "0.2.0",
"prismjs": "1.16.0",
Expand Down
21 changes: 0 additions & 21 deletions website/src/mixins/doc.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,4 @@
import jump from 'jump.js'

export default {
watch: {
'$route.hash': {
handler(hash) {
this.$nextTick(() => {
if (hash) {
const el = document.getElementById(hash.slice(1))
if (el) {
jump(el, {
duration: 0,
offset: -(document.querySelector('.header').clientHeight + 20)
})
}
}
})
},
immediate: true
}
},

mounted() {
// Make footnotes focusable
const items = this.$el.querySelectorAll('.footnote-item,.footnote-ref a')
Expand Down
5 changes: 0 additions & 5 deletions website/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,6 @@ indexes-of@^1.0.1:
resolved "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=

[email protected]:
version "1.0.2"
resolved "https://registry.npmjs.org/jump.js/-/jump.js-1.0.2.tgz#e0641b47f40a38f2139c25fda0500bf28e43015a"
integrity sha1-4GQbR/QKOPITnCX9oFAL8o5DAVo=

lodash._reinterpolate@~3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
Expand Down