diff --git a/.changeset/honest-islands-flash.md b/.changeset/honest-islands-flash.md new file mode 100644 index 000000000000..b87fbad1aa05 --- /dev/null +++ b/.changeset/honest-islands-flash.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[breaking] `tabindex="-1"` is no longer added to ``; `` only briefly receives it during navigation diff --git a/packages/kit/src/runtime/client/renderer.js b/packages/kit/src/runtime/client/renderer.js index 3c0db40a66eb..9732283b647a 100644 --- a/packages/kit/src/runtime/client/renderer.js +++ b/packages/kit/src/runtime/client/renderer.js @@ -369,8 +369,24 @@ export class Renderer { const { scroll, keepfocus } = opts; if (!keepfocus) { + // Reset page selection and focus + // We try to mimick browsers' behaviour as closely as possible by targeting the + // viewport, but unfortunately it's not a perfect match — e.g. shift-tabbing won't + // immediately cycle from the end of the page + // See https://html.spec.whatwg.org/multipage/interaction.html#get-the-focusable-area + const root = document.documentElement; + const tabindex = root.getAttribute('tabindex'); + getSelection()?.removeAllRanges(); - document.body.focus(); + root.tabIndex = -1; + root.focus(); + + // restore `tabindex` as to prevent the document from stealing input from elements + if (tabindex !== null) { + root.setAttribute('tabindex', tabindex); + } else { + root.removeAttribute('tabindex'); + } } // need to render the DOM before we can scroll to the rendered elements diff --git a/packages/kit/src/runtime/client/router.js b/packages/kit/src/runtime/client/router.js index 1f2351f35b70..4c581d90bcc5 100644 --- a/packages/kit/src/runtime/client/router.js +++ b/packages/kit/src/runtime/client/router.js @@ -73,9 +73,6 @@ export class Router { this.enabled = true; this.initialized = false; - // make it possible to reset focus - document.body.setAttribute('tabindex', '-1'); - // keeping track of the history index in order to prevent popstate navigation events if needed this.current_history_index = history.state?.['sveltekit:index'] ?? 0; diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index ec1305efab19..7c99311be30d 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -25,6 +25,8 @@ test.describe.parallel('a11y', () => { await page.keyboard.press('Tab'); expect(await page.evaluate(() => (document.activeElement || {}).nodeName)).toBe('A'); expect(await page.evaluate(() => (document.activeElement || {}).textContent)).toBe('a'); + + expect(await page.evaluate(() => document.documentElement.getAttribute('tabindex'))).toBe(null); }); test('announces client-side navigation', async ({ page, clicknav, javaScriptEnabled }) => { diff --git a/sites/kit.svelte.dev/src/lib/search/SearchBox.svelte b/sites/kit.svelte.dev/src/lib/search/SearchBox.svelte index b42c7e92995e..8cf93195a3ed 100644 --- a/sites/kit.svelte.dev/src/lib/search/SearchBox.svelte +++ b/sites/kit.svelte.dev/src/lib/search/SearchBox.svelte @@ -57,7 +57,9 @@ const scroll = -parseInt(document.body.style.top || '0'); document.body.style.position = ''; document.body.style.top = ''; + document.body.tabIndex = -1; document.body.focus(); + document.body.removeAttribute('tabindex'); window.scrollTo(0, scroll); } }