Skip to content

Commit e3410cc

Browse files
mrkishibenmccann
andauthored
restore tabindex after navigation (#4140)
* restore `tabindex` after navigation * fix focus issue in the docs * add comments * Update packages/kit/src/runtime/client/renderer.js Co-authored-by: Ben McCann <[email protected]> Co-authored-by: Ben McCann <[email protected]>
1 parent 8c08aa5 commit e3410cc

File tree

5 files changed

+26
-4
lines changed

5 files changed

+26
-4
lines changed

.changeset/honest-islands-flash.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+
[breaking] `tabindex="-1"` is no longer added to `<body>`; `<html>` only briefly receives it during navigation

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,8 +369,24 @@ export class Renderer {
369369
const { scroll, keepfocus } = opts;
370370

371371
if (!keepfocus) {
372+
// Reset page selection and focus
373+
// We try to mimick browsers' behaviour as closely as possible by targeting the
374+
// viewport, but unfortunately it's not a perfect match — e.g. shift-tabbing won't
375+
// immediately cycle from the end of the page
376+
// See https://html.spec.whatwg.org/multipage/interaction.html#get-the-focusable-area
377+
const root = document.documentElement;
378+
const tabindex = root.getAttribute('tabindex');
379+
372380
getSelection()?.removeAllRanges();
373-
document.body.focus();
381+
root.tabIndex = -1;
382+
root.focus();
383+
384+
// restore `tabindex` as to prevent the document from stealing input from elements
385+
if (tabindex !== null) {
386+
root.setAttribute('tabindex', tabindex);
387+
} else {
388+
root.removeAttribute('tabindex');
389+
}
374390
}
375391

376392
// need to render the DOM before we can scroll to the rendered elements

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,6 @@ export class Router {
7373
this.enabled = true;
7474
this.initialized = false;
7575

76-
// make it possible to reset focus
77-
document.body.setAttribute('tabindex', '-1');
78-
7976
// keeping track of the history index in order to prevent popstate navigation events if needed
8077
this.current_history_index = history.state?.['sveltekit:index'] ?? 0;
8178

packages/kit/test/apps/basics/test/test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ test.describe.parallel('a11y', () => {
2525
await page.keyboard.press('Tab');
2626
expect(await page.evaluate(() => (document.activeElement || {}).nodeName)).toBe('A');
2727
expect(await page.evaluate(() => (document.activeElement || {}).textContent)).toBe('a');
28+
29+
expect(await page.evaluate(() => document.documentElement.getAttribute('tabindex'))).toBe(null);
2830
});
2931

3032
test('announces client-side navigation', async ({ page, clicknav, javaScriptEnabled }) => {

sites/kit.svelte.dev/src/lib/search/SearchBox.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@
5757
const scroll = -parseInt(document.body.style.top || '0');
5858
document.body.style.position = '';
5959
document.body.style.top = '';
60+
document.body.tabIndex = -1;
6061
document.body.focus();
62+
document.body.removeAttribute('tabindex');
6163
window.scrollTo(0, scroll);
6264
}
6365
}

0 commit comments

Comments
 (0)