Skip to content

Commit e3b698e

Browse files
committed
reset focus properly on navigation (#307)
1 parent e09b6be commit e3b698e

File tree

7 files changed

+48
-14
lines changed

7 files changed

+48
-14
lines changed

.changeset/tidy-books-reply.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+
Reset focus properly

packages/kit/src/runtime/internal/router/index.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ export class Router {
124124

125125
const selected = this.select(new URL(location.href));
126126
if (selected) return this.renderer.start(selected);
127+
128+
// make it possible to reset focus
129+
document.body.setAttribute('tabindex', '-1');
127130
}
128131

129132
select(url) {
@@ -177,9 +180,7 @@ export class Router {
177180

178181
await this.renderer.render(selected);
179182

180-
if (document.activeElement instanceof HTMLElement) {
181-
document.activeElement.blur();
182-
}
183+
document.body.focus();
183184

184185
const deep_linked = hash && document.getElementById(hash.slice(1));
185186
if (scroll) {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<nav><a href="/focus/a">a</a> <a href="/focus/b">b</a></nav>
2+
3+
<slot />
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import * as assert from 'uvu/assert';
2+
3+
export default function (test) {
4+
test.only('resets focus', async ({ visit, click, keyboard, evaluate, contains }) => {
5+
await visit('/focus/a');
6+
7+
await click('[href="/focus/b"]');
8+
assert.ok(await contains('b'));
9+
assert.equal(await evaluate(() => document.activeElement.nodeName), 'BODY');
10+
await keyboard.press('Tab');
11+
assert.equal(await evaluate(() => document.activeElement.nodeName), 'A');
12+
assert.equal(await evaluate(() => document.activeElement.textContent), 'a');
13+
14+
await click('[href="/focus/a"]');
15+
assert.ok(await contains('a'));
16+
assert.equal(await evaluate(() => document.activeElement.nodeName), 'BODY');
17+
await keyboard.press('Tab');
18+
assert.equal(await evaluate(() => document.activeElement.nodeName), 'A');
19+
assert.equal(await evaluate(() => document.activeElement.textContent), 'a');
20+
});
21+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<p>a</p>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<p>b</p>

test/runner.js

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ async function setup({ port }) {
1515
await page
1616
.waitForFunction(
1717
({ expectedValue, selector }) =>
18-
document.querySelector(selector) && document.querySelector(selector).textContent === expectedValue,
18+
document.querySelector(selector) &&
19+
document.querySelector(selector).textContent === expectedValue,
1920
{ expectedValue, selector },
2021
{ timeout: defaultTimeout }
2122
)
@@ -28,7 +29,7 @@ async function setup({ port }) {
2829

2930
const capture_requests = async (operations) => {
3031
const requests = [];
31-
const on_request = request => requests.push(request.url());
32+
const on_request = (request) => requests.push(request.url());
3233
page.on('request', on_request);
3334

3435
try {
@@ -56,9 +57,9 @@ async function setup({ port }) {
5657

5758
return {
5859
base,
59-
visit: path => page.goto(base + path),
60-
contains: async str => (await page.innerHTML('body')).includes(str),
61-
html: async selector => await page.innerHTML(selector, { timeout: defaultTimeout }),
60+
visit: (path) => page.goto(base + path),
61+
contains: async (str) => (await page.innerHTML('body')).includes(str),
62+
html: async (selector) => await page.innerHTML(selector, { timeout: defaultTimeout }),
6263
fetch: (url, opts) => fetch(`${base}${url}`, opts),
6364
text,
6465
evaluate: (fn) => page.evaluate(fn),
@@ -73,9 +74,10 @@ async function setup({ port }) {
7374
wait_for_function: (fn, arg, options) =>
7475
page.waitForFunction(fn, arg, { timeout: defaultTimeout, ...options }),
7576
capture_requests,
76-
set_extra_http_headers: headers => page.setExtraHTTPHeaders(headers),
77+
set_extra_http_headers: (headers) => page.setExtraHTTPHeaders(headers),
7778
pathname: () => page.url().replace(base, ''),
78-
$: selector => page.$(selector)
79+
keyboard: page.keyboard,
80+
$: (selector) => page.$(selector)
7981
};
8082
}
8183

@@ -88,15 +90,15 @@ export function runner(callback, options = {}) {
8890

8991
const duplicate = (test_fn) => {
9092
return (name, callback) => {
91-
test_fn(`${name} [no js]`, async context => {
93+
test_fn(`${name} [no js]`, async (context) => {
9294
await callback({
9395
...context,
9496
js: false
9597
});
9698
});
9799

98100
if (!options.amp) {
99-
test_fn(`${name} [js]`, async context => {
101+
test_fn(`${name} [js]`, async (context) => {
100102
await callback({
101103
...context,
102104
js: true,
@@ -108,8 +110,8 @@ export function runner(callback, options = {}) {
108110
});
109111
});
110112
}
111-
}
112-
}
113+
};
114+
};
113115

114116
const test = duplicate(suite);
115117
test.skip = duplicate(suite.skip);

0 commit comments

Comments
 (0)