diff --git a/src/core/event/index.js b/src/core/event/index.js index 8b8c275b12..7c36a10ad5 100644 --- a/src/core/event/index.js +++ b/src/core/event/index.js @@ -1,11 +1,26 @@ import {isMobile} from '../util/env' import {body, on} from '../util/dom' import * as sidebar from './sidebar' -import {scrollIntoView} from './scroll' +import {scrollIntoView, scroll2Top} from './scroll' export function eventMixin(proto) { - proto.$resetEvents = function () { - scrollIntoView(this.route.path, this.route.query.id) + proto.$resetEvents = function (source) { + const {auto2top} = this.config + + ;(() => { + // Rely on the browser's scroll auto-restoration when going back or forward + if (source === 'history') { + return + } + // Scroll to ID if specified + if (this.route.query.id) { + scrollIntoView(this.route.path, this.route.query.id) + } + // Scroll to top if a link was clicked and auto2top is enabled + if (source === 'navigate') { + auto2top && scroll2Top(auto2top) + } + })(); if (this.config.loadNavbar) { sidebar.getAndActive(this.router, 'nav') diff --git a/src/core/fetch/index.js b/src/core/fetch/index.js index fb4f0d20a8..fca67faf3f 100644 --- a/src/core/fetch/index.js +++ b/src/core/fetch/index.js @@ -145,7 +145,7 @@ export function fetchMixin(proto) { } } - proto.$fetch = function (cb = noop) { + proto.$fetch = function (cb = noop, $resetEvents = this.$resetEvents.bind(this)) { const done = () => { callHook(this, 'doneEach') cb() @@ -157,7 +157,7 @@ export function fetchMixin(proto) { done() } else { this._fetch(() => { - this.$resetEvents() + $resetEvents(); done() }) } diff --git a/src/core/render/index.js b/src/core/render/index.js index 5df3c4cd55..8adb9a45b5 100644 --- a/src/core/render/index.js +++ b/src/core/render/index.js @@ -8,7 +8,7 @@ import {getAndActive, sticky} from '../event/sidebar' import {getPath, isAbsolutePath} from '../router/util' import {isMobile, inBrowser} from '../util/env' import {isPrimitive} from '../util/core' -import {scrollActiveSidebar, scroll2Top} from '../event/scroll' +import {scrollActiveSidebar} from '../event/scroll' import {prerenderEmbed} from './embed' function executeScript() { @@ -109,7 +109,7 @@ export function renderMixin(proto) { } proto._bindEventOnRendered = function (activeEl) { - const {autoHeader, auto2top} = this.config + const {autoHeader} = this.config scrollActiveSidebar(this.router) @@ -122,8 +122,6 @@ export function renderMixin(proto) { dom.before(main, h1) } } - - auto2top && scroll2Top(auto2top) } proto._renderNav = function (text) { diff --git a/src/core/router/history/hash.js b/src/core/router/history/hash.js index 4e1b1a3617..659b779896 100644 --- a/src/core/router/history/hash.js +++ b/src/core/router/history/hash.js @@ -30,7 +30,25 @@ export class HashHistory extends History { } onchange(cb = noop) { - on('hashchange', cb) + // The hashchange event does not tell us if it originated from + // a clicked link or by moving back/forward in the history; + // therefore we set a `navigating` flag when a link is clicked + // to be able to tell these two scenarios apart + let navigating = false + + on('click', e => { + const el = e.target.tagName === 'A' ? e.target : e.target.parentNode + + if (el.tagName === 'A' && !/_blank/.test(el.target)) { + navigating = true + } + }) + + on('hashchange', e => { + const source = navigating ? 'navigate' : 'history' + navigating = false + cb({event: e, source}) + }) } normalize() { diff --git a/src/core/router/history/html5.js b/src/core/router/history/html5.js index a3bda13918..b207753b94 100644 --- a/src/core/router/history/html5.js +++ b/src/core/router/history/html5.js @@ -28,11 +28,13 @@ export class HTML5History extends History { e.preventDefault() const url = el.href window.history.pushState({ key: url }, '', url) - cb() + cb({ event: e, source: 'navigate' }) } }) - on('popstate', cb) + on('popstate', e => { + cb({ event: e, source: 'history' }) + }) } /** diff --git a/src/core/router/index.js b/src/core/router/index.js index 943fe6c3eb..0d6fa6e4c8 100644 --- a/src/core/router/index.js +++ b/src/core/router/index.js @@ -1,6 +1,7 @@ import {HashHistory} from './history/hash' import {HTML5History} from './history/html5' import {supportsPushState} from '../util/env' +import {noop} from '../util/core' import * as dom from '../util/dom' export function routerMixin(proto) { @@ -30,16 +31,16 @@ export function initRouter(vm) { updateRender(vm) lastRoute = vm.route - router.onchange(_ => { + router.onchange(params => { updateRender(vm) vm._updateRender() if (lastRoute.path === vm.route.path) { - vm.$resetEvents() + vm.$resetEvents(params.source) return } - vm.$fetch() + vm.$fetch(noop, vm.$resetEvents.bind(vm, params.source)) lastRoute = vm.route }) }