From 34f18ae83ca130e02bcf504401a0b103dc0612a0 Mon Sep 17 00:00:00 2001 From: Bjorn Lu <34116392+bluwy@users.noreply.github.com> Date: Fri, 5 Nov 2021 12:16:24 +0800 Subject: [PATCH] [fix] reset scroll when navigated from scrolled page (#2735) --- .changeset/wet-papayas-live.md | 5 ++ packages/kit/src/runtime/client/renderer.js | 48 +++++++++++-------- .../anchor-with-manual-scroll/_tests.js | 10 ++-- .../apps/basics/src/routes/anchor/_tests.js | 23 ++++++--- .../basics/src/routes/anchor/index.svelte | 9 +++- .../basics/src/routes/use-action/_tests.js | 10 ++-- packages/kit/test/test.js | 19 ++++++++ packages/kit/test/types.d.ts | 4 ++ 8 files changed, 87 insertions(+), 41 deletions(-) create mode 100644 .changeset/wet-papayas-live.md diff --git a/.changeset/wet-papayas-live.md b/.changeset/wet-papayas-live.md new file mode 100644 index 000000000000..cf93f3b4448c --- /dev/null +++ b/.changeset/wet-papayas-live.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[fix] reset scroll when navigated from scrolled page diff --git a/packages/kit/src/runtime/client/renderer.js b/packages/kit/src/runtime/client/renderer.js index b0990138ecf8..b0d8838b8115 100644 --- a/packages/kit/src/runtime/client/renderer.js +++ b/packages/kit/src/runtime/client/renderer.js @@ -275,28 +275,34 @@ export class Renderer { this._init(navigation_result); } - if (!opts?.keepfocus) { - document.body.focus(); - } + if (!opts) { + await 0; + } else { + const { hash, scroll, keepfocus } = opts; - await 0; - - // After `await 0`, the onMount() function in the component executed. - // If there was no scrolling happening (checked via pageYOffset), - // continue on our custom scroll handling - if (pageYOffset === 0 && opts) { - const { hash, scroll } = opts; - - const deep_linked = hash && document.getElementById(hash.slice(1)); - if (scroll) { - scrollTo(scroll.x, scroll.y); - } else if (deep_linked) { - // Here we use `scrollIntoView` on the element instead of `scrollTo` - // because it natively supports the `scroll-margin` and `scroll-behavior` - // CSS properties. - deep_linked.scrollIntoView(); - } else { - scrollTo(0, 0); + if (!keepfocus) { + document.body.focus(); + } + + const oldPageYOffset = pageYOffset; + await 0; + const maxPageYOffset = document.body.scrollHeight - innerHeight; + + // After `await 0`, the `onMount()` function in the component executed. + // If there was no scrolling happening (checked via `pageYOffset`), + // continue on our custom scroll handling + if (pageYOffset === Math.min(oldPageYOffset, maxPageYOffset)) { + const deep_linked = hash && document.getElementById(hash.slice(1)); + if (scroll) { + scrollTo(scroll.x, scroll.y); + } else if (deep_linked) { + // Here we use `scrollIntoView` on the element instead of `scrollTo` + // because it natively supports the `scroll-margin` and `scroll-behavior` + // CSS properties. + deep_linked.scrollIntoView(); + } else { + scrollTo(0, 0); + } } } diff --git a/packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/_tests.js b/packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/_tests.js index ebc787a52896..fd10490913da 100644 --- a/packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/_tests.js +++ b/packages/kit/test/apps/basics/src/routes/anchor-with-manual-scroll/_tests.js @@ -9,10 +9,9 @@ export default function (test) { test( 'url-supplied anchor is ignored with onMount() scrolling on direct page load', '/anchor-with-manual-scroll/anchor#go-to-element', - async ({ page, js }) => { + async ({ is_intersecting_viewport, js }) => { if (js) { - const p = await page.$('#abcde'); - assert.ok(p && (await p.isVisible())); + assert.ok(is_intersecting_viewport('#abcde')); } } ); @@ -20,11 +19,10 @@ export default function (test) { test( 'url-supplied anchor is ignored with onMount() scrolling on navigation to page', '/anchor-with-manual-scroll', - async ({ page, clicknav, js }) => { + async ({ clicknav, is_intersecting_viewport, js }) => { await clicknav('[href="/anchor-with-manual-scroll/anchor#go-to-element"]'); if (js) { - const p = await page.$('#abcde'); - assert.ok(p && (await p.isVisible())); + assert.ok(is_intersecting_viewport('#abcde')); } } ); diff --git a/packages/kit/test/apps/basics/src/routes/anchor/_tests.js b/packages/kit/test/apps/basics/src/routes/anchor/_tests.js index 660bf1457a14..3ddc93ea97f1 100644 --- a/packages/kit/test/apps/basics/src/routes/anchor/_tests.js +++ b/packages/kit/test/apps/basics/src/routes/anchor/_tests.js @@ -9,10 +9,9 @@ export default function (test) { test( 'url-supplied anchor works on direct page load', '/anchor/anchor#go-to-element', - async ({ page, js }) => { + async ({ is_intersecting_viewport, js }) => { if (js) { - const p = await page.$('#go-to-element'); - assert.ok(p && (await p.isVisible())); + assert.ok(is_intersecting_viewport('#go-to-element')); } } ); @@ -20,11 +19,21 @@ export default function (test) { test( 'url-supplied anchor works on navigation to page', '/anchor', - async ({ page, clicknav, js }) => { - await clicknav('[href="/anchor/anchor#go-to-element"]'); + async ({ clicknav, is_intersecting_viewport, js }) => { + await clicknav('#first-anchor'); if (js) { - const p = await page.$('#go-to-element'); - assert.ok(p && (await p.isVisible())); + assert.ok(is_intersecting_viewport('#go-to-element')); + } + } + ); + + test( + 'url-supplied anchor works when navigated from scrolled page', + '/anchor', + async ({ clicknav, is_intersecting_viewport, js }) => { + await clicknav('#second-anchor'); + if (js) { + assert.ok(is_intersecting_viewport('#go-to-element')); } } ); diff --git a/packages/kit/test/apps/basics/src/routes/anchor/index.svelte b/packages/kit/test/apps/basics/src/routes/anchor/index.svelte index b89b04ad2874..960cf6547774 100644 --- a/packages/kit/test/apps/basics/src/routes/anchor/index.svelte +++ b/packages/kit/test/apps/basics/src/routes/anchor/index.svelte @@ -1,5 +1,7 @@