From 5f22d4fa39171906802cc20ada00ec57bdfce880 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Tue, 5 May 2020 11:42:56 +0200 Subject: [PATCH] feat(scroll): scroll to the same location like regular links --- e2e/specs/scroll-behavior.js | 20 +++++++++++---- src/history/html5.ts | 4 ++- src/router.ts | 50 +++++++++++++++++++----------------- 3 files changed, 45 insertions(+), 29 deletions(-) diff --git a/e2e/specs/scroll-behavior.js b/e2e/specs/scroll-behavior.js index 8fe376469..4deab3290 100644 --- a/e2e/specs/scroll-behavior.js +++ b/e2e/specs/scroll-behavior.js @@ -122,11 +122,9 @@ module.exports = { .click('li:nth-child(4) a') .assert.evaluate( function () { - // TODO: change implementation to use `afterEach` - return true - // return ( - // document.getElementById('anchor').getBoundingClientRect().top < 1 - // ) + return ( + document.getElementById('anchor').getBoundingClientRect().top < 1 + ) }, null, 'scroll to anchor when the route is the same' @@ -199,6 +197,18 @@ module.exports = { 'scroll to anchor' ) + .url('http://localhost:8080/scroll-behavior/bar#anchor') + .waitForElementPresent('.view.bar', TIMEOUT) + .assert.evaluate( + function () { + return ( + document.getElementById('anchor').getBoundingClientRect().top < 1 + ) + }, + null, + 'scroll to anchor when directly navigating to it' + ) + .end() }, } diff --git a/src/history/html5.ts b/src/history/html5.ts index 0e72ae0df..731892862 100644 --- a/src/history/html5.ts +++ b/src/history/html5.ts @@ -187,7 +187,9 @@ function useHistoryStateNavigation(base: string) { // the length is off by one, we need to decrease it position: history.length - 1, replaced: true, - scroll: computeScrollPosition(), + // don't add a scroll as the user may have an anchor and we want + // scrollBehavior to be triggered without a saved position + scroll: null, }, true ) diff --git a/src/router.ts b/src/router.ts index cb8da2ece..4393d41ba 100644 --- a/src/router.ts +++ b/src/router.ts @@ -388,11 +388,23 @@ export function createRouter(options: RouterOptions): Router { toLocation.redirectedFrom = redirectedFrom let failure: NavigationFailure | void | undefined - if (!force && isSameRouteLocation(from, targetLocation)) + if (!force && isSameRouteLocation(from, targetLocation)) { failure = createRouterError( ErrorTypes.NAVIGATION_DUPLICATED, { to: toLocation, from } ) + // trigger scroll to allow scrolling to the same anchor + handleScroll( + from, + from, + // this is a push, the only way for it to be triggered from a + // history.listen is with a redirect, which makes it become a pus + true, + // This cannot be the first navigation because the initial location + // cannot be manually navigated to + false + ) + } return (failure ? Promise.resolve(failure) : navigate(toLocation, from)) .catch((error: NavigationFailure | NavigationRedirectError) => { @@ -609,23 +621,7 @@ export function createRouter(options: RouterOptions): Router { // accept current navigation currentRoute.value = toLocation - // TODO: call handleScroll in afterEach so it can also be triggered on - // duplicated navigation (e.g. same anchor navigation). It needs exposing - // the navigation information (type, direction) - if (isBrowser) { - // if we are pushing, we cannot have a saved position. This is important - // when visiting /b from /a, scrolling, going back to /a by with the back - // button and then clicking on a link to /b instead of the forward button - const savedScroll = - !isPush && getSavedScrollPosition(getScrollKey(toLocation.fullPath, 0)) - handleScroll( - toLocation, - from, - savedScroll || ((isFirstNavigation || !isPush) && state && state.scroll) - ).catch(err => { - triggerError(err) - }) - } + handleScroll(toLocation, from, isPush, isFirstNavigation) markAsReady() } @@ -751,17 +747,25 @@ export function createRouter(options: RouterOptions): Router { } // Scroll behavior - function handleScroll( to: RouteLocationNormalizedLoaded, from: RouteLocationNormalizedLoaded, - scrollPosition?: Required - ) { - if (!scrollBehavior) return Promise.resolve() + isPush: boolean, + isFirstNavigation: boolean + ): Promise { + if (!isBrowser || !scrollBehavior) return Promise.resolve() + + let scrollPosition: Required | null = + (!isPush && getSavedScrollPosition(getScrollKey(to.fullPath, 0))) || + ((isFirstNavigation || !isPush) && + (history.state as HistoryState) && + history.state.scroll) || + null return nextTick() - .then(() => scrollBehavior!(to, from, scrollPosition || null)) + .then(() => scrollBehavior!(to, from, scrollPosition)) .then(position => position && scrollToPosition(position)) + .catch(triggerError) } function go(delta: number) {