diff --git a/src/index.js b/src/index.js index 0d536633..6acc1086 100644 --- a/src/index.js +++ b/src/index.js @@ -102,6 +102,13 @@ export default class ScrollBehavior { }, }; + // In case no scrolling occurs, save the initial position + if (!scrollElement.savePositionHandle) { + scrollElement.savePositionHandle = requestAnimationFrame( + saveElementPosition, + ); + } + this._scrollElements[key] = scrollElement; on(element, 'scroll', scrollElement.onScroll); @@ -126,7 +133,15 @@ export default class ScrollBehavior { } updateScroll(prevContext, context) { - this._updateWindowScroll(prevContext, context); + this._updateWindowScroll(prevContext, context).then(() => { + // Save the position immediately after a transition so that if no + // scrolling occurs, there is still a saved position + if (!this._saveWindowPositionHandle) { + this._saveWindowPositionHandle = requestAnimationFrame( + this._saveWindowPosition, + ); + } + }); Object.keys(this._scrollElements).forEach(key => { this._updateElementScroll(key, prevContext, context); @@ -216,7 +231,7 @@ export default class ScrollBehavior { // scroll it isn't enough. Instead, try to scroll a few times until it // works. this._numWindowScrollAttempts = 0; - this._checkWindowScrollPosition(); + return this._checkWindowScrollPosition(); } _updateElementScroll(key, prevContext, context) { @@ -282,7 +297,7 @@ export default class ScrollBehavior { // Still, check anyway just in case. /* istanbul ignore if: paranoid guard */ if (!this._windowScrollTarget) { - return; + return Promise.resolve(); } this.scrollToTarget(window, this._windowScrollTarget); @@ -291,13 +306,16 @@ export default class ScrollBehavior { /* istanbul ignore if: paranoid guard */ if (this._numWindowScrollAttempts >= MAX_SCROLL_ATTEMPTS) { + // This might happen if the scroll position was already set to the target this._windowScrollTarget = null; - return; + return Promise.resolve(); } - this._checkWindowScrollHandle = requestAnimationFrame( - this._checkWindowScrollPosition, - ); + return new Promise(resolve => { + this._checkWindowScrollHandle = requestAnimationFrame(() => + resolve(this._checkWindowScrollPosition()), + ); + }); }; scrollToTarget(element, target) { diff --git a/test/ScrollBehavior.test.js b/test/ScrollBehavior.test.js index 90e56a2d..98eff09f 100644 --- a/test/ScrollBehavior.test.js +++ b/test/ScrollBehavior.test.js @@ -141,6 +141,32 @@ describe('ScrollBehavior', () => { }, ]); }); + + it('should save position even if it does not change', done => { + const history = withRoutes( + withScroll(createHistory(), (prevLoc, loc) => + loc.action === 'PUSH' ? [10, 20] : true, + ), + ); + + unlisten = run(history, [ + () => { + history.push('/detail'); + }, + () => { + history.push('/'); + }, + () => { + history.push('/detail'); + }, + () => history.goBack(), + () => { + expect(scrollLeft(window)).to.equal(10); + expect(scrollTop(window)).to.equal(20); + done(); + }, + ]); + }); }); describe('scroll element', () => { diff --git a/test/run.js b/test/run.js index 3de86763..e790151d 100644 --- a/test/run.js +++ b/test/run.js @@ -1,6 +1,8 @@ export function delay(cb) { // Give throttled scroll listeners time to settle down. - requestAnimationFrame(() => requestAnimationFrame(cb)); + requestAnimationFrame(() => + requestAnimationFrame(() => requestAnimationFrame(cb)), + ); } export default function run(history, steps) {