Let’s say a user clicks on a button. If the button directs the user’s attention to something, you want to direct keyboard focus to that thing as well.
For example, let’s say we have an off-canvas menu. When a user clicks on the button, a menu pops out. We direct attention to the menu with an animation.
You want to direct focus to the menu for keyboard users. If the user presses Tab, you want to focus on the first item in the menu.
You can direct focus with Element.focus
Element.focus()
Element.focus
Element.focus lets you direct focus to focusable elements. By default, this means:
Links
Buttons
Form elements
In the example below, if you click on the second button, the focus gets directed to the first button.
<button>Button</button>
<button>Go to previous button</button>
Note: Focus also gets directed if the user interacts with the button with Space or Enter. This is because Space and Enter keys trigger a click event when they’re used on buttons.
Directing focus to non-focusable elements
Element.focus can only direct focus to focusable elements. It cannot direct focus to non-focusable elements. The code below does nothing. Focus remains on <button>.
<p>The quick brown fox jumps over the lazy dog</p>
<button>Go to paragraph</button>
If you want to direct focus to a non-focusable element, you need to add tabindex to the element. In the example below, the paragraph gets focus when users click on the button.
<p tabindex="-1">The quick brown fox jumps over the lazy dog</p>
<button>Go to paragraph</button>
Directing focus with links
Links let you direct focus when:
Its href begins with a #.
Its href points to an id of an element on the same page.
You can direct focus to focusable elements.
<a href="#button">Jump to button</a>
<button id="button">Button</button>
You can also direct focus to non-focusable elements. This feature is unique to links. These four things happen when you direct the focus to a non-focusable element.
Focus goes to body.
Further keypresses originate from the <body>.
But you can tab to the next focusable element.
You can also shift-tab to the previous focusable element.
It’s best to show you with an example. Let’s say you have the following HTML:
<button>First Button</button>
<article id="article">
<h1>The quick brown fox jumps over the lazy dog</h1>
<p>But the lazy dog refuses to move. It continues sleeping.</p>
<button>Second Button</button>
<button>Third Button</button>
</article>
<a href="#article">Jump to article</a>
Here, the link points to #article, but #article is not focusable.
If you click the link, the focus goes to <body>. We know this because document.activeElement is <body>. In the GIF below, I tabbed to the button before clicking on the link. This shows you that focus goes to <body>.
Any keypress after clicking on the link originates from <body>. We can test this with an event listener.
You can still tab to the next focusable element (in this case, it’s Second Button).
You can also shift-tab to the previous focusable element (in this case, it’s First Button).
We use this feature to let users jump around a page. Here’s an example where I let users jump to a section they’re interested in.
Focus and scroll
If you focus on an element, the browser will jump to the element. This is true with both Element.focus and links.
If you want to disable the auto-scrolling behavior, you can pass preventScroll: true to Element.focus.
Element.focus({preventScroll: true})
If you want to disable the auto-scrolling behavior on links, you need to use preventDefault. This prevents all default behavior present on links.
const a = document.querySelector('a')
// Don't do this unless necessary
a.addEventListener('click', event => {
event.preventDefault()
})
In my opinion, you should allow browsers to scroll to the focused element. It doesn’t make sense to focus on an element that’s outside of the viewport.