Directing focus

Hey ,

I'm thrilled to help you learn JavaScript. Unfortunately, you've landed on a page where you cannot access with your current purchase.

Please upgrade (use this link) access this content.

I'm super eager to help you learn more!

Directing focus

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.

Removed focus from offsiteContainer.

You can direct focus with Element.focus

Element.focus()

Element.focus

Element.focus lets you direct focus to focusable elements. By default, this means:

  1. Links
  2. Buttons
  3. 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>
const buttons = document.querySelectorAll('button')
const firstButton = buttons[0]
const secondButton = buttons[1]

secondButton.addEventListener('click', event => {
  firstButton.focus()
})
Focus directed to first button when user clicks on the second 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.

Directs focus to first button when second button gets clicked.

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>
const button = document.querySelector('button')
const paragraph = document.querySelector('p')

button.addEventListener('click', event => {
  paragraph.focus()
})
Focus remains on button. It does not get directed to the paragraph.

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>
Focus directed to paragraph when user clicks on button.

Links let you direct focus when:

  1. Its href begins with a #.
  2. 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>
Focuses on button when user clicks the link.

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.

  1. Focus goes to body.
  2. Further keypresses originate from the <body>.
  3. But you can tab to the next focusable element.
  4. 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>.

Focus goes to body.

Any keypress after clicking on the link originates from <body>. We can test this with an event listener.

document.addEventListener('keydown', event => {
  console.log(event.target)
})
Further keypresses originate from the body element.

You can still tab to the next focusable element (in this case, it’s Second Button).

Tab to the next focusable element.

You can also shift-tab to the previous focusable element (in this case, it’s First Button).

Shift-tab to the previous focusable element.

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.

Jumping to a section of the content.

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.

Browers jump to the focused element.

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.