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:
- Does clicking focus the button?
- After clicking, does keypresses originate from the button?
- After clicking, can we tab to the next element?
- 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:
- There's no focus glow on the button.
-
document.activeElement
points to<body>
.
Since <body>
gets focus, any further keypress originates from the <body>
.
Tabbing into the next element works as expected. The next element gets focus.
Shift
+ Tab
doesn't work as I expected. I expect the previous element to get focus, but <button>
gets focus 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.
Any further keypress originates from the <body>
.
Tab
does not work as expected. When you press Tab
, Firefox focuses on the first element in the document.
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.
The problem with Firefox and buttons date back to Firefox 63 (at least). MDN has a section on this:
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.
Further keypress originates from the <button>
.
Tab works as expected. The 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.
The next keypress originates from <button>
. This is expected since <button>
is the focused element.
Tab
works as expected. The next element gets focus.
Shift
+ Tab
works as expected. The previous element gets focus.
Chrome (Windows)
When you click on a button in Chrome (Chrome 75.0), the button gets focus.
The next keypress originates from <button>
.
Tab
works as expected. The next element gets focus.
Shift
+ Tab
works as expected. The previous element gets focus.
Edge (Windows)
When you click on a button in Edge (Edge 17), the button gets focus, but the focus ring did not appear.
The next keypress originates from <button>
.
Tab
works as expected. The next element gets focus.
Shift
+ Tab
works as expected. The previous element gets focus.
Summary of the results
We tested for four things across the common browsers:
- Does clicking focus the button?
- After clicking, does keypresses originate from the button?
- After clicking, can we tab to the next element?
- After clicking, can we shift-tab to the previous element?
Here are the results in a table form.
Test | Safari | Firefox () | 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 element | First element in document | Next element | Next element | Next element | Next element |
Shift + Tab goes to: | <button> |
Previously focused element (if any) | Previous Element | Previous Element | Previous Element | Previous Element |
You can see the inconsistencies here. It's clear as day. The major inconsistencies are:
- Firefox on Mac is simply weird. Everything seems wrong.
- Some browsers don't focus on the button when they're clicked.
- 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.
- When you click on a button, focus should be on the button.
- Button should have a focus glow.
- When you press
Tab
after clicking a button, the next element should get focus. - 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', event => {
if (event.target.matches('button') {
event.target.focus()
}
})
This code focuses on the button when you click on it. This also makes sure:
- The focus glow appears.
- Tab goes to the next element.
- 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!).
Thanks for reading. This article was originally posted on my blog. Sign up for my newsletter if you want more articles to help you become a better frontend developer.