Skip to content

Commit 5006435

Browse files
teemingcdummdidumm
andauthored
fix: defer when clicking hash links with data-sveltekit-reload (#12866)
fixes #12582 fixes #12188 This PR ensures that clicking on a hash link with the reload option avoids the usual behaviour of thinking the page will reload and instead allows it to be handled as if the option wasn't enabled (hash links on the same page never reload the page). It also fixes a behaviour where an element doesn't get focused when clicking on the hash link more than once. --------- Co-authored-by: Simon H <[email protected]>
1 parent bcb30cd commit 5006435

File tree

7 files changed

+50
-4
lines changed

7 files changed

+50
-4
lines changed

.changeset/chilled-cats-hang.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
fix: ensure element is focused after subsequent clicks of the same hash link

.changeset/strange-buckets-sell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
fix: avoid reloading behaviour for hash links with data-sveltekit-reload if the hash is on the same page

packages/kit/src/runtime/client/client.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2089,8 +2089,11 @@ function _start_router() {
20892089

20902090
if (download) return;
20912091

2092+
const [nonhash, hash] = url.href.split('#');
2093+
const same_pathname = nonhash === strip_hash(location);
2094+
20922095
// Ignore the following but fire beforeNavigate
2093-
if (external || options.reload) {
2096+
if (external || (options.reload && (!same_pathname || !hash))) {
20942097
if (_before_navigate({ url, type: 'link' })) {
20952098
// set `navigating` to `true` to prevent `beforeNavigate` callbacks
20962099
// being called when the page unloads
@@ -2105,8 +2108,7 @@ function _start_router() {
21052108
// Check if new url only differs by hash and use the browser default behavior in that case
21062109
// This will ensure the `hashchange` event is fired
21072110
// Removing the hash does a full page navigation in the browser, so make sure a hash is present
2108-
const [nonhash, hash] = url.href.split('#');
2109-
if (hash !== undefined && nonhash === strip_hash(location)) {
2111+
if (hash !== undefined && same_pathname) {
21102112
// If we are trying to navigate to the same hash, we should only
21112113
// attempt to scroll to that element and avoid any history changes.
21122114
// Otherwise, this can cause Firefox to incorrectly assign a null
@@ -2121,7 +2123,11 @@ function _start_router() {
21212123
if (hash === '' || (hash === 'top' && a.ownerDocument.getElementById('top') === null)) {
21222124
window.scrollTo({ top: 0 });
21232125
} else {
2124-
a.ownerDocument.getElementById(decodeURIComponent(hash))?.scrollIntoView();
2126+
const element = a.ownerDocument.getElementById(decodeURIComponent(hash));
2127+
if (element) {
2128+
element.scrollIntoView();
2129+
element.focus();
2130+
}
21252131
}
21262132

21272133
return;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<a href="#example" data-sveltekit-reload>focus</a>
2+
<input id="example" />
3+
<a href="/data-sveltekit/reload/hash/new">new page</a>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<p>hello world</p>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<a href="#example">focus</a>
2+
<input id="example" />

packages/kit/test/apps/basics/test/cross-platform/client.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,30 @@ test.describe('Routing', () => {
715715
expect(await page.textContent('#page-url-hash')).toBe('#target');
716716
});
717717

718+
test('clicking on a hash link focuses the associated element', async ({ page }) => {
719+
await page.goto('/routing/hashes/focus');
720+
await page.locator('a[href="#example"]').click();
721+
await expect(page.getByRole('textbox')).toBeFocused();
722+
// check it still works when the hash is already present in the URL
723+
await page.locator('a[href="#example"]').click();
724+
await expect(page.getByRole('textbox')).toBeFocused();
725+
});
726+
727+
test('backwards navigation works after clicking a hash link with data-sveltekit-reload', async ({
728+
page,
729+
clicknav,
730+
baseURL
731+
}) => {
732+
await page.goto('/data-sveltekit/reload/hash');
733+
await page.locator('a[href="#example"]').click();
734+
expect(page.url()).toBe(`${baseURL}/data-sveltekit/reload/hash#example`);
735+
await clicknav('a[href="/data-sveltekit/reload/hash/new"]');
736+
expect(page.url()).toBe(`${baseURL}/data-sveltekit/reload/hash/new`);
737+
await page.goBack();
738+
expect(page.url()).toBe(`${baseURL}/data-sveltekit/reload/hash#example`);
739+
await expect(page.getByRole('textbox')).toBeVisible();
740+
});
741+
718742
test('back button returns to previous route when previous route has been navigated to via hash anchor', async ({
719743
page,
720744
clicknav

0 commit comments

Comments
 (0)