Just Use a Button

3 days ago 2

One of the weirdest “debates” I seem to perpetually have with framework-enthusiastic developers is whether or not a <div> is “just as good” as a <button>.

Spoiler: it’s not. Let’s dig in.

The problem

Among the React crowd, and also among people who seem to enjoy HTMX, I see a lot this…

<div onclick="showSignIn()"> Open Modal </div>
function showSignIn () { // Code to show the sign-in modal. // The details of what happens here vary by stack. }

What’s wrong with this?

  1. This element does not announce itself as an interactive element to screen reader users.
  2. You can’t focus on a <div> with a keyboard.
  3. The event only fires on click, not when the Enter or Space Bar keys are pressed (again, keyboard users).

I’ve seen this in a lot of code bases. I’ve seen it in a lot of demos.

I’ve had arguments with a very prominent React thought leader whose name starts with R who insisted that using a <div> was “more accessible” than using a <button>, and that Twitter made the right decision in using this pattern in their app.

A middle-aged woman with brown hair and a purple shirt saying 'That's not how it works! That's not how any of this works!' to her two female-presenting friends.

It’s wrong. It’s all wrong.

The “fixes” aren’t

Many HTML elements have implicit roles that tell assistive tech like screen readers what they do.

The <button> element is one of them. It has an implicit [role] of button, which tells screen reader users it can be interacted with and will trigger some type of behavior in the app.

The HTML [role] attribute can be used to add or modify the role of an element. And so, folks like React Ry–thought-leader-guy will say stuff like (I’m paraphrasing)…

That attribute exists for a reason. You can add [role="button"] to a div to give it the correct semantics.

OK, that addresses one issue.

That role doesn’t affect focusability (or lack thereof) or keyboard behavior. Visually impaired users and people who navigate with a keyboard still can’t use it.

“No worries!” they say. “We can fix that, too!”

You can make the element focusable with the [tabindex] attribute.

<div onclick="showSignIn()" tabindex="0" > Open Modal </div>

You shouldn’t, though! Seriously, just don’t fuck with focus order.

It’s way too easy to go down this path and then fuck it up and have folks jumping all over the page instead of navigating through in the normal and expected order.

Dr. Ian Malcom from Jurassic Park saying, 'Your scientists were so preoccupied with whether they could, they didn't stop to think if they should.'

And again, still no keyboard interactivity.

But don’t fear! You can add that, too. You just need to listen for all keydown events, and then filter them out by event.key so that you only run your code if the Enter or Spacebar keys were pressed (the latter means checking for a literal space: ' ').

That can’t run on the element, either. You’ve got to attach that even to the document and figure out which element has focus.

document.addEventListener('keydown', (event) => { // Only run on Enter and Spacebar presses if (event.key !== 'Enter' & event.key !== ' ') return; // Make sure the element you care about how focus const notRealBtn = document.activeElement.closest('[onclick]'); if (!notRealBtn) return; // Run your code, somehow... });

So um… ok, I guess it is technically a fix, but…

You’ve just recreated all of the functionality a <button> gives you for free

Seriously, WTF would you do that?!?

All of these hoops to write this HTML…

<div onclick="showSignIn()" tabindex="0" > Open Modal </div>

When you could write this HTML instead…

<button onclick="showSignIn()"> Open Modal </button>

A <button>…

  1. Has the correct [role] implicitly.
  2. Is automatically focusable.
  3. Fires a click event in response to Enter and Spacebar presses when it has focus.

Look, I’m a lazy developer.

And I suspect, if you’re someone who loves tools like React, you probably are, too. It’s cool, I get it! The best code is the code you didn’t write and all that.

So, be even lazier.

Use the correct element for the job, and avoid writing a bunch of extra code!

Read Entire Article