Skip to content

Commit 5992501

Browse files
committed
support scroll to anchor in scrollBehavior
1 parent 885ac18 commit 5992501

File tree

4 files changed

+58
-30
lines changed

4 files changed

+58
-30
lines changed

examples/scroll-behavior/app.js

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,36 @@ Vue.use(VueRouter)
55

66
const Home = { template: '<div>home</div>' }
77
const Foo = { template: '<div>foo</div>' }
8-
const Bar = { template: '<div>bar</div>' }
8+
const Bar = {
9+
template: `
10+
<div>
11+
bar
12+
<div style="height:500px"></div>
13+
<p id="anchor">Anchor</p>
14+
</div>
15+
`
16+
}
917

1018
// scrollBehavior:
1119
// - only available in html5 history mode
12-
// - defaults to false
13-
// - if set to true, remembers and restores scroll position on popstate
14-
// - or use a function to conditionally return a position
15-
const scrollBehavior = (to, from, isPopState) => {
16-
if (!isPopState || to.matched.some(m => m.meta.scrollToTop)) {
17-
// explicitly control scroll behavior
18-
// scroll to top on new navigations or routes that requires scrolling to top
19-
return { x: 0, y: 0 }
20+
// - defaults to no scroll behavior
21+
// - return false to prevent scroll
22+
const scrollBehavior = (to, from, savedPosition) => {
23+
if (savedPosition) {
24+
// savedPosition is only available for popstate navigations.
25+
return savedPosition
26+
} else {
27+
// new navigation.
28+
// scroll to anchor
29+
if (to.hash) {
30+
return { anchor: true }
31+
}
32+
// explicitly control scroll position
33+
// check if any matched route config has meta that requires scrolling to top
34+
if (to.matched.some(m => m.meta.scrollToTop)) {
35+
return { x: 0, y: 0 }
36+
}
2037
}
21-
// for popstate navigations, use saved position
22-
return true
2338
}
2439

2540
const router = new VueRouter({
@@ -42,6 +57,7 @@ new Vue({
4257
<li><router-link to="/">/</router-link></li>
4358
<li><router-link to="/foo">/foo</router-link></li>
4459
<li><router-link to="/bar">/bar</router-link></li>
60+
<li><router-link to="/bar#anchor">/bar#anchor</router-link></li>
4561
</ul>
4662
<router-view class="view"></router-view>
4763
</div>

examples/scroll-behavior/index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
<style>
44
.view {
55
border: 1px solid red;
6-
height: 1000px;
6+
height: 2000px;
7+
position: relative;
78
}
89
</style>
910
<a href="/">&larr; Examples index</a>

flow/declarations.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ declare type RouterOptions = {
1010
mode?: string;
1111
base?: string;
1212
linkActiveClass?: string;
13-
scrollBehavior?: boolean | () => boolean | Object;
13+
scrollBehavior?: Function;
1414
}
1515

1616
declare type RedirectOption = string | { name: string }

src/history/html5.js

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* @flow */
22

33
import type VueRouter from '../index'
4+
import { assert } from '../util/warn'
45
import { cleanPath } from '../util/path'
56
import { History } from './base'
67

@@ -64,30 +65,40 @@ export class HTML5History extends History {
6465
return
6566
}
6667

67-
let shouldScroll = false
6868
const behavior = router.options.scrollBehavior
69-
if (typeof behavior === 'boolean') {
70-
shouldScroll = behavior
71-
} else if (typeof behavior === 'function') {
72-
shouldScroll = behavior(to, from, isPop)
69+
if (!behavior) {
70+
return
7371
}
7472

73+
assert(typeof behavior === 'function', `scrollBehavior must be a function`)
74+
75+
let position = getScrollPosition()
76+
const shouldScroll = behavior(to, from, isPop ? position : null)
7577
if (!shouldScroll) {
7678
return
7779
}
7880

79-
let position = getScrollPosition()
80-
if (typeof shouldScroll === 'object' &&
81-
shouldScroll.x != null &&
82-
shouldScroll.y != null) {
83-
position = shouldScroll
84-
}
85-
if (position) {
86-
const { x, y } = position
87-
router.app.$nextTick(() => {
88-
window.scrollTo(x, y)
89-
})
90-
}
81+
// wait until re-render finishes before scrolling
82+
router.app.$nextTick(() => {
83+
const isObject = typeof shouldScroll === 'object'
84+
if (isObject && shouldScroll.x != null && shouldScroll.y != null) {
85+
position = shouldScroll
86+
} else if (isObject && shouldScroll.anchor) {
87+
const el = document.querySelector(to.hash)
88+
if (el) {
89+
const docTop = document.documentElement.getBoundingClientRect().top
90+
const elTop = el.getBoundingClientRect().top
91+
position = {
92+
x: window.scrollX,
93+
y: elTop - docTop
94+
}
95+
}
96+
}
97+
98+
if (position) {
99+
window.scrollTo(position.x, position.y)
100+
}
101+
})
91102
}
92103
}
93104

0 commit comments

Comments
 (0)