Skip to content

Commit 23060c9

Browse files
author
Mate Solymosi
committed
feat(scroll): smarter scroll behavior
1 parent dc43d3c commit 23060c9

File tree

6 files changed

+52
-15
lines changed

6 files changed

+52
-15
lines changed

Diff for: src/core/event/index.js

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
11
import { isMobile } from '../util/env';
22
import { body, on } from '../util/dom';
33
import * as sidebar from './sidebar';
4-
import { scrollIntoView } from './scroll';
4+
import { scrollIntoView, scroll2Top } from './scroll';
55

66
export function eventMixin(proto) {
7-
proto.$resetEvents = function() {
8-
scrollIntoView(this.route.path, this.route.query.id);
7+
proto.$resetEvents = function(source) {
8+
const { auto2top } = this.config;
9+
10+
(() => {
11+
// Rely on the browser's scroll auto-restoration when going back or forward
12+
if (source === 'history') {
13+
return;
14+
}
15+
// Scroll to ID if specified
16+
if (this.route.query.id) {
17+
scrollIntoView(this.route.path, this.route.query.id);
18+
}
19+
// Scroll to top if a link was clicked and auto2top is enabled
20+
if (source === 'navigate') {
21+
auto2top && scroll2Top(auto2top);
22+
}
23+
})();
924

1025
if (this.config.loadNavbar) {
1126
sidebar.getAndActive(this.router, 'nav');

Diff for: src/core/fetch/index.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,10 @@ export function fetchMixin(proto) {
148148
}
149149
};
150150

151-
proto.$fetch = function(cb = noop) {
151+
proto.$fetch = function(
152+
cb = noop,
153+
$resetEvents = this.$resetEvents.bind(this)
154+
) {
152155
const done = () => {
153156
callHook(this, 'doneEach');
154157
cb();
@@ -160,7 +163,7 @@ export function fetchMixin(proto) {
160163
done();
161164
} else {
162165
this._fetch(() => {
163-
this.$resetEvents();
166+
$resetEvents();
164167
done();
165168
});
166169
}

Diff for: src/core/render/index.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { getAndActive, sticky } from '../event/sidebar';
66
import { getPath, isAbsolutePath } from '../router/util';
77
import { isMobile, inBrowser } from '../util/env';
88
import { isPrimitive } from '../util/core';
9-
import { scrollActiveSidebar, scroll2Top } from '../event/scroll';
9+
import { scrollActiveSidebar } from '../event/scroll';
1010
import { Compiler } from './compiler';
1111
import * as tpl from './tpl';
1212
import { prerenderEmbed } from './embed';
@@ -123,7 +123,7 @@ export function renderMixin(proto) {
123123
};
124124

125125
proto._bindEventOnRendered = function(activeEl) {
126-
const { autoHeader, auto2top } = this.config;
126+
const { autoHeader } = this.config;
127127

128128
scrollActiveSidebar(this.router);
129129

@@ -136,8 +136,6 @@ export function renderMixin(proto) {
136136
dom.before(main, wrapper.children[0]);
137137
}
138138
}
139-
140-
auto2top && scroll2Top(auto2top);
141139
};
142140

143141
proto._renderNav = function(text) {

Diff for: src/core/router/history/hash.js

+19-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,25 @@ export class HashHistory extends History {
3030
}
3131

3232
onchange(cb = noop) {
33-
on('hashchange', cb);
33+
// The hashchange event does not tell us if it originated from
34+
// a clicked link or by moving back/forward in the history;
35+
// therefore we set a `navigating` flag when a link is clicked
36+
// to be able to tell these two scenarios apart
37+
let navigating = false;
38+
39+
on('click', e => {
40+
const el = e.target.tagName === 'A' ? e.target : e.target.parentNode;
41+
42+
if (el.tagName === 'A' && !/_blank/.test(el.target)) {
43+
navigating = true;
44+
}
45+
});
46+
47+
on('hashchange', e => {
48+
const source = navigating ? 'navigate' : 'history';
49+
navigating = false;
50+
cb({ event: e, source });
51+
});
3452
}
3553

3654
normalize() {

Diff for: src/core/router/history/html5.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@ export class HTML5History extends History {
2828
e.preventDefault();
2929
const url = el.href;
3030
window.history.pushState({ key: url }, '', url);
31-
cb();
31+
cb({ event: e, source: 'navigate' });
3232
}
3333
});
3434

35-
on('popstate', cb);
35+
on('popstate', e => {
36+
cb({ event: e, source: 'history' });
37+
});
3638
}
3739

3840
/**

Diff for: src/core/router/index.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { supportsPushState } from '../util/env';
22
import * as dom from '../util/dom';
33
import { HashHistory } from './history/hash';
44
import { HTML5History } from './history/html5';
5+
import { noop } from '../util/core';
56

67
export function routerMixin(proto) {
78
proto.route = {};
@@ -31,16 +32,16 @@ export function initRouter(vm) {
3132
lastRoute = vm.route;
3233

3334
// eslint-disable-next-line no-unused-vars
34-
router.onchange(_ => {
35+
router.onchange(params => {
3536
updateRender(vm);
3637
vm._updateRender();
3738

3839
if (lastRoute.path === vm.route.path) {
39-
vm.$resetEvents();
40+
vm.$resetEvents(params.source);
4041
return;
4142
}
4243

43-
vm.$fetch();
44+
vm.$fetch(noop, vm.$resetEvents.bind(vm, params.source));
4445
lastRoute = vm.route;
4546
});
4647
}

0 commit comments

Comments
 (0)