Inconsistent behavior among browsers when clicking on buttons

Published:

I noticed browsers were inconsistent in how they handle a click on <button>. Some browsers choose to focus on the button. Some browsers don’t.

In this article, I want to show you my test and findings. Then, I want to talk about a way to overcome these inconsistencies.

The test

The test is simple. We’re testing what happens when we click on a <button>. Specifically, we want to know if:

  1. Does clicking focus the button?
  2. After clicking, does keypresses originate from the button?
  3. After clicking, can we tab to the next element?
  4. After clicking, can we shift-tab to the previous element?

Here’s the HTML we’re using for the test:

<div tabindex="0">Placeholder for testing tab</div>
<button>Button</button>
<div tabindex="0">Placeholder for testing tab</div>

The <div>s are there for us to test tabbing and shift-tabbing easily.

Here’s a Codepen for you if you want to follow along with the tests.

See the Pen Button and link focus test by Zell Liew (@zellwk) on CodePen.

Testing for focus

We can test for focus visually. If the button gets focused, there should be the default visual glow around the button.

We can also test for focus programmatically. In JavaScript, you can get the focused element with document.activeElement. When we click a button, we can log the focused element.

const button = document.querySelector('button')
button.addEventListener('click', event => {
  console.log('Click:', document.activeElement)
})

Note: If you’re using Chrome, you can use the Live Expression tool (so there’s no need to log document.activeElement).

Testing for keypress

Here, we can add a keydown event listener to the document. Here, we want to log what element triggered the event. We can tell the element with event.target.

document.addEventListener('keydown', event => {
  console.log(`Keydown:`, event.target)
})

Testing for Tab and Shift-tab

After clicking on a button, does Tab go to the next focusable element? If it goes to the next focusable element, that element should receive a focus outline.

Likewise, does Shift + Tab goes to the previous focusable element? If it goes to the previous focusable element, that element should receive a focus outline too.

I did not log document.activeElement because the focus glow is enough.

Results

Safari (Mac)

When you click on a button in Safari (12.1.1), the button does not get focus. The document gets focus instead. We know this because:

  1. There’s no focus glow on the button.
  2. document.activeElement points to <body>.
In Safari, document gets focused when you click on a button

Since <body> gets focus, any further keypress originates from the <body>.

Next keypress originate from the document.

Tabbing into the next element works as expected. The next element gets focus.

Tabbing into the next element works as expected.

Shift + Tab doesn’t work as I expected. I expect the previous element to get focus, but <button> gets focus instead.

Shift tab does not focus on the previous element. It focuses on the button instead.

Firefox (Mac)

When you click on a button in Firefox (Nightly 70.0a1), the button does not get focus. The document gets focus instead.

In Firefox, document gets focused when you click on a button

Any further keypress originates from the <body>.

Next keypress originate from the document.

Tab does not work as expected. When you press Tab, Firefox focuses on the first element in the document.

Tab goes to the first element.

Shift + Tab is funny. If <button> is the first thing you clicked on, Firefox focuses on the last focusable element in the document. If you focused on an element before clicking the button, Firefox focuses that element.

Shift-tab behavior.

The problem with Firefox and buttons date back to Firefox 63 (at least). MDN has a section on this:

MDN's documentation regarding button clicks in Firefox and other browsers.

Firefox (Windows)

When you click on a button in Firefox (Quantum 68.0.1, Windows version), the button gets focus, but the focus glow does not show up.

Buttons gets focus but the focus glow does not show up.

Further keypress originates from the <button>.

Next keypress originate from the button.

Tab works as expected. The next item gets focus.

Next item gets focus.

Shift + Tab works as expected. The previous item gets focus.

Chrome (Mac)

When you click on a button in Chrome (Canary 78.0), the button gets focus. This implementation is different from Safari and Firefox.

In Chrome, the button gets focus when you click on it.

The next keypress originates from <button>. This is expected since <button> is the focused element.

Next keypress originates from the button

Tab works as expected. The next element gets focus.

Next element gets focus when you tab

Shift + Tab works as expected. The previous element gets focus.

Previous element gets focus when you press Shift + Tab

Chrome (Windows)

When you click on a button in Chrome (Chrome 75.0), the button gets focus.

The button gets focus when you click on it.

The next keypress originates from <button>.

Next keypress originates from the button

Tab works as expected. The next element gets focus.

Next element gets focus when you tab

Shift + Tab works as expected. The previous element gets focus.

Previous element gets focus when you press Shift + Tab

Edge (Windows)

When you click on a button in Edge (Edge 17), the button gets focus, but the focus ring did not appear.

The button gets focus when you click on it, but the focus ring did not appear.

The next keypress originates from <button>.

Next keypress originates from the button

Tab works as expected. The next element gets focus.

Next element gets focus when you tab

Shift + Tab works as expected. The previous element gets focus.

Previous element gets focus when you press Shift + Tab

Summary of the results

We tested for four things across the common browsers:

  1. Does clicking focus the button?
  2. After clicking, does keypresses originate from the button?
  3. After clicking, can we tab to the next element?
  4. After clicking, can we shift-tab to the previous element?

Here are the results in a table form.

TestSafariFirefox ()Firefox (⊞)Chrome ()Chrome (⊞)Edge (⊞)
Focused element<body><body><button> (but no focus glow)<button><button><button> (but no focus glow)
Next Keypress from:<body><body><button><button><button><button>
Tab goes to:Next elementFirst element in documentNext elementNext elementNext elementNext element
Shift + Tab goes to:<button>Previously focused element (if any)Previous ElementPrevious ElementPrevious ElementPrevious Element

You can see the inconsistencies here. It’s clear as day. The major inconsistencies are:

  1. Firefox on Mac is simply weird. Everything seems wrong.
  2. Some browsers don’t focus on the button when they’re clicked.
  3. Some browsers don’t include a focus glow on the button when they’re clicked.

The HTML Spec doesn’t state what browsers should do after a user clicks on a button. So no browsers are at fault for the inconsistent behavior.

Here’s a potential fix

I think Chrome’s implementation (both Mac and Windows) makes the most sense.

  1. When you click on a button, focus should be on the button.
  2. Button should have a focus glow.
  3. When you press Tab after clicking a button, the next element should get focus.
  4. When you press Shift + Tab after clicking a button, the previous element should get focus.

Note: If you’re the kind of person that hates the default focus style, you can restyle the focus ring (or you can wait for :focus-visible to be widely supported).

There’s a quick fix if you want to make the other browsers behave consistently with Chrome’s implementation. All you have to do is add this code at the top of your JavaScript.

document.addEventListener('click', function (event) {
  if (event.target.matches('button')) {
    event.target.focus()
  }
})

This code focuses on the button when you click on it. This also makes sure:

  1. The focus glow appears.
  2. Tab goes to the next element.
  3. Shift-Tab goes to the previous element

Important note: You want to put this code AT THE TOP of your JavaScript files. It works because event listeners are called in the order they’re declared. Focus will always go to the button first. You can then redirect focus to other elements if you desire.

Important note #2: I have not tested this code thoroughly with all devices yet. (Only Mac versions Safari, Firefox, and Chrome). I appreciate it if you can help to conduct some tests. Let me know if I’m wrong in any way. Thanks.

In case you were wondering why I did these tests: I realized the inconsistent behavior when I was writing the Keyboard section for Learn JavaScript. I did these tests because I wanted to teach my students the right way to handle buttons and focus (which is a big part of accessibility!).

Want to become a better Frontend Developer?

Don’t worry about where to start. I’ll send you a library of articles frontend developers have found useful!

  • 60+ CSS articles
  • 90+ JavaScript articles

I’ll also send you one article every week to help you improve your FED skills crazy fast!