Skip to content

Commit ce13b55

Browse files
homerjamyyx990803
authored andcommitted
feat: scrollBehavior accept returning a promise (#1758)
1 parent a722b6a commit ce13b55

File tree

5 files changed

+86
-42
lines changed

5 files changed

+86
-42
lines changed

examples/scroll-behavior/app.js

+36-24
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import VueRouter from 'vue-router'
33

44
Vue.use(VueRouter)
55

6-
const Home = { template: '<div>home</div>' }
7-
const Foo = { template: '<div>foo</div>' }
6+
const Home = { template: '<div class="home">home</div>' }
7+
const Foo = { template: '<div class="foo">foo</div>' }
88
const Bar = {
99
template: `
10-
<div>
10+
<div class="bar">
1111
bar
1212
<div style="height:500px"></div>
1313
<p id="anchor" style="height:500px">Anchor</p>
@@ -20,32 +20,42 @@ const Bar = {
2020
// - only available in html5 history mode
2121
// - defaults to no scroll behavior
2222
// - return false to prevent scroll
23-
const scrollBehavior = (to, from, savedPosition) => {
23+
const scrollBehavior = function (to, from, savedPosition) {
2424
if (savedPosition) {
2525
// savedPosition is only available for popstate navigations.
2626
return savedPosition
2727
} else {
28-
const position = {}
29-
// new navigation.
30-
// scroll to anchor by returning the selector
31-
if (to.hash) {
32-
position.selector = to.hash
28+
return new Promise(resolve => {
29+
const position = {}
30+
let delay = 500
31+
// new navigation.
32+
// scroll to anchor by returning the selector
33+
if (to.hash) {
34+
position.selector = to.hash
3335

34-
// specify offset of the element
35-
if (to.hash === '#anchor2') {
36-
position.offset = { y: 100 }
36+
// specify offset of the element
37+
if (to.hash === '#anchor2') {
38+
position.offset = { y: 100 }
39+
}
40+
41+
if (document.querySelector(to.hash)) {
42+
delay = 0
43+
}
44+
}
45+
// check if any matched route config has meta that requires scrolling to top
46+
if (to.matched.some(m => m.meta.scrollToTop)) {
47+
// coords will be used if no selector is provided,
48+
// or if the selector didn't match any element.
49+
position.x = 0
50+
position.y = 0
3751
}
38-
}
39-
// check if any matched route config has meta that requires scrolling to top
40-
if (to.matched.some(m => m.meta.scrollToTop)) {
41-
// cords will be used if no selector is provided,
42-
// or if the selector didn't match any element.
43-
position.x = 0
44-
position.y = 0
45-
}
46-
// if the returned position is falsy or an empty object,
47-
// will retain current scroll position.
48-
return position
52+
// wait for the out transition to complete (if necessary)
53+
setTimeout(() => {
54+
// if the returned position is falsy or an empty object,
55+
// will retain current scroll position.
56+
resolve(position)
57+
}, delay)
58+
})
4959
}
5060
}
5161

@@ -72,7 +82,9 @@ new Vue({
7282
<li><router-link to="/bar#anchor">/bar#anchor</router-link></li>
7383
<li><router-link to="/bar#anchor2">/bar#anchor2</router-link></li>
7484
</ul>
75-
<router-view class="view"></router-view>
85+
<transition name="fade" mode="out-in">
86+
<router-view class="view"></router-view>
87+
</transition>
7688
</div>
7789
`
7890
}).$mount('#app')

examples/scroll-behavior/index.html

+6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
<!DOCTYPE html>
22
<link rel="stylesheet" href="/global.css">
33
<style>
4+
.fade-enter-active, .fade-leave-active {
5+
transition: opacity .5s ease;
6+
}
7+
.fade-enter, .fade-leave-active {
8+
opacity: 0
9+
}
410
.view {
511
border: 1px solid red;
612
height: 2000px;

src/util/scroll.js

+28-16
Original file line numberDiff line numberDiff line change
@@ -38,27 +38,19 @@ export function handleScroll (
3838

3939
// wait until re-render finishes before scrolling
4040
router.app.$nextTick(() => {
41-
let position = getScrollPosition()
41+
const position = getScrollPosition()
4242
const shouldScroll = behavior(to, from, isPop ? position : null)
43+
4344
if (!shouldScroll) {
4445
return
4546
}
46-
const isObject = typeof shouldScroll === 'object'
47-
if (isObject && typeof shouldScroll.selector === 'string') {
48-
const el = document.querySelector(shouldScroll.selector)
49-
if (el) {
50-
let offset = shouldScroll.offset && typeof shouldScroll.offset === 'object' ? shouldScroll.offset : {}
51-
offset = normalizeOffset(offset)
52-
position = getElementPosition(el, offset)
53-
} else if (isValidPosition(shouldScroll)) {
54-
position = normalizePosition(shouldScroll)
55-
}
56-
} else if (isObject && isValidPosition(shouldScroll)) {
57-
position = normalizePosition(shouldScroll)
58-
}
5947

60-
if (position) {
61-
window.scrollTo(position.x, position.y)
48+
if (typeof shouldScroll.then === 'function') {
49+
shouldScroll.then(shouldScroll => {
50+
scrollToPosition(shouldScroll, position)
51+
})
52+
} else {
53+
scrollToPosition(shouldScroll, position)
6254
}
6355
})
6456
}
@@ -111,3 +103,23 @@ function normalizeOffset (obj: Object): Object {
111103
function isNumber (v: any): boolean {
112104
return typeof v === 'number'
113105
}
106+
107+
function scrollToPosition (shouldScroll, position) {
108+
const isObject = typeof shouldScroll === 'object'
109+
if (isObject && typeof shouldScroll.selector === 'string') {
110+
const el = document.querySelector(shouldScroll.selector)
111+
if (el) {
112+
let offset = shouldScroll.offset && typeof shouldScroll.offset === 'object' ? shouldScroll.offset : {}
113+
offset = normalizeOffset(offset)
114+
position = getElementPosition(el, offset)
115+
} else if (isValidPosition(shouldScroll)) {
116+
position = normalizePosition(shouldScroll)
117+
}
118+
} else if (isObject && isValidPosition(shouldScroll)) {
119+
position = normalizePosition(shouldScroll)
120+
}
121+
122+
if (position) {
123+
window.scrollTo(position.x, position.y)
124+
}
125+
}

test/e2e/nightwatch.config.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,12 @@ module.exports = {
3333
'desiredCapabilities': {
3434
'browserName': 'chrome',
3535
'javascriptEnabled': true,
36-
'acceptSslCerts': true
36+
'acceptSslCerts': true,
37+
'chromeOptions': {
38+
'args': [
39+
'window-size=1280,800'
40+
]
41+
}
3742
}
3843
},
3944

test/e2e/specs/scroll-behavior.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
module.exports = {
22
'scroll behavior': function (browser) {
3+
const TIMEOUT = 2000
4+
35
browser
46
.url('http://localhost:8080/scroll-behavior/')
57
.waitForElementVisible('#app', 1000)
@@ -10,11 +12,13 @@ module.exports = {
1012
window.scrollTo(0, 100)
1113
})
1214
.click('li:nth-child(2) a')
15+
.waitForElementPresent('.view.foo', TIMEOUT)
1316
.assert.containsText('.view', 'foo')
1417
.execute(function () {
1518
window.scrollTo(0, 200)
1619
window.history.back()
1720
})
21+
.waitForElementPresent('.view.home', TIMEOUT)
1822
.assert.containsText('.view', 'home')
1923
.assert.evaluate(function () {
2024
return window.pageYOffset === 100
@@ -45,6 +49,7 @@ module.exports = {
4549
window.scrollTo(0, 50)
4650
window.history.forward()
4751
})
52+
.waitForElementPresent('.view.foo', TIMEOUT)
4853
.assert.containsText('.view', 'foo')
4954
.assert.evaluate(function () {
5055
return window.pageYOffset === 200
@@ -53,12 +58,14 @@ module.exports = {
5358
.execute(function () {
5459
window.history.back()
5560
})
61+
.waitForElementPresent('.view.home', TIMEOUT)
5662
.assert.containsText('.view', 'home')
5763
.assert.evaluate(function () {
5864
return window.pageYOffset === 50
5965
}, null, 'restore scroll position on back again')
6066

6167
.click('li:nth-child(3) a')
68+
.waitForElementPresent('.view.bar', TIMEOUT)
6269
.assert.evaluate(function () {
6370
return window.pageYOffset === 0
6471
}, null, 'scroll to top on new entry')
@@ -68,7 +75,9 @@ module.exports = {
6875
return document.getElementById('anchor').getBoundingClientRect().top < 1
6976
}, null, 'scroll to anchor')
7077

71-
.click('li:nth-child(5) a')
78+
.execute(function () {
79+
document.querySelector('li:nth-child(5) a').click()
80+
})
7281
.assert.evaluate(function () {
7382
return document.getElementById('anchor2').getBoundingClientRect().top < 101
7483
}, null, 'scroll to anchor with offset')

0 commit comments

Comments
 (0)