Of course it does. tabindex=0 doesn't sort naturally into the automatic tabindex order, it sorts AFTER everything. So you are jumping through all the other tabindex elements, then you are jumping back to all tabindex=0.
The issue isn't with tabindex=0 specifically, but fucking with tabindex in general. People go down that path, and start putting that shit on everything, like it's Frank's Red Hot.
And in my experience, the same folks who use div's instead of button's are the ones who don't know better and start throwing tabindex around.
"why do you need to listen for events at the document level?"
Not events generally, keydown events specifically, which do not fire on child elements of the document.
Is this actually true nowadays? Given that advice like this is often parrotted by people who don't actually use screen-reading software, I sometimes wonder if this is a situation where we've just been saying this and repeating this advice; meanwhile, screen readers have obviously become sophisticated enough to recognize that a div with an onclick handler on it is probably, you know, clickable and interactive.
It has the "onclick" property set though. The other two points are pretty valid, however. It's a shame we don't have a proper "onsubmit" property or something like that. "Oninteract"?
It's generally advised not to set tabindex to anything but 0 or -1 and let the document order dictate tab order.
https://developer.mozilla.org/en-US/docs/Web/API/Element/key...
I'm sure there are some devs who reach for a div because they're unaware of the proper way to disable submit behavior.
(One example: https://heydonworks.com/article/aria-controls-is-poop)
Yes, but often you have elements with taborder > 0.
> It's generally advised not to set tabindex to anything but 0 or -1 and let the document order dictate tab order.
Only if document order is sane. Usually with modern websites it isn't, document order is a broken notion if you can position elements at will and e.g. put navigation at the bottom of a document but move it to the top by CSS. Which is actually a recommendation that some people make for acessibility...
What you usually want to do is assign a sensible taborder > 0 to the one form element that the user is probably currently using. Otherwise, he will pointlessly tab through search, menus, cookie bars and a ton of other pointless stuff first.
* works with middle click for new tab
* integrates with accessibility devices
* works with right click + open in new window or similar options
* etc. etc. etc.
If it's notionally navigation, don't use javascript soup: use a link.
Are you sure? I have a 17 year old HTML tool written using plain, vanilla JavaScript where keydown on a child element seems to have been working as expected.
https://susam.net/quickqwerty.html
https://github.com/susam/quickqwerty/blob/1.2.0/quickqwerty....
Nice article, by the way!
Are you sure? Screen readers should be able to detect a div with a onclick as interactable, no? And if they can’t, that seems like an exceedingly simple fix. I’d be shocked if they can’t already detect onclick.
Screen readers also don't access the DOM directly, there's an extra abstraction layer. Browsers expose accessibility data to the appropriate OS API, and screen readers use the data exposed by the OS. There's too much variation as-is between different screen readers to be piling browser-specific behavior on top.
You can just as easily apply the same tabindex to a div though.
> Only if document order is sane. Usually with modern websites it isn't...
Well that's the real problem, all your non-interactive content (like text) is going to be out of order too. You're just adding to the confusion if buttons and other inputs are in a different order from the content they're associated with.
> Otherwise, he will pointlessly tab through search, menus, cookie bars and a ton of other pointless stuff first.
The proper way of dealing with this is a "Skip to Main Content" element:
In my day job's shared library, we made a Clickable component that basically exists to abstract away the decision to use a button or an anchor tag, and resets the styles so both elements act the same way (both by default and when we apply styles to each).
We'd have a lot of confusion on the design side about button-as-design vs button-as-function and now we don't have to deal with that at all anymore.
And since the styling's been reset in a predictable way, it takes away one of the bigger reasons why people go to divs in the first place.
The web is darn simple, but we are the place where it is made extremely over engineered and expensive for both companies (salaries and 2-3x more staff needed than necessary because of the bloat) and users of their products (in terms of payloads).
And yet JS-heavy frameworks seems to have the best job market.
Everything seems to be upside down.
That is an issue in favor of DIV, you don't accidentally set type="submit" because you don't know about that. There are many things many people don't know.
Using a DIV means you start from empty plate and explicitly add the features you want to add, which makes it easy to later remove or change those features when you want to.
Of course if your knowledge of HTML standards is great and Button exactly fits your current use-case you probably will use it, but designs and requirements change all the time.
Using DIV makes your design more transparent, you see there are certain features you have set to it by adding the attributes.
Using a Button means you (and other people reading your code) probably want to consult documentation to understand what it exactly does. With a DIV + attributes that is explicitly "spelled out".
Just some pros and cons.
> Note: While <input> elements of type button are still perfectly valid HTML, the newer <button> element is now the favored way to create buttons. Given that a <button>'s label text is inserted between the opening and closing tags, you can include HTML in the label, even images.
See this fiddle https://jsfiddle.net/483uqjnp/
(again, I do not condone building your own <button>, just pointing this out)
No, it isn't the proper way. That only works if you can see the skip link and know to press enter. Otherwise you will tab straight into the navigation. So possibly useful for screen readers, but completely useless for most keyboard users. Yet another stupid webdev workaround for a selfimposed problem.
What you should do is autofocus the first form element (if there is a form), give it tabindex=1 and number the other form elements in a sensible ascending tabindex order. Otherwise, proper semantic markup is sufficient, even for screen readers.
The other thing I'd do is add `aria-controls=folder0` to the button that toggles visibility of the list with `id=folder0`
https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/...
Maybe you’re thinking of <input type=“button”>, which doesn’t submit?
To anyone reading who has tried to get fancy with a substitute for the <a> tag, I wish you mild discomfort and inconvenience.
Yes, the default is bad, but you should be overriding every one of those anyway.
So if you use a div, you _always_ need to add _all_ of the features. Whereas if you use a button, you usually need to remember to add the correct "type" attribute (although if you're building a form, you might not even need that).
You also mention transparency. A button makes your design transparent: it is a standard element, and web developers should know what it does. A div is not transparent - firstly, if I'm a developer reading that code, I need to look at context clues to understand what the authors' intent was (a tabbable element with a keydown handler and a click handler could be all sorts of elements, not just a button, so now I need to inspect event handlers and see what they do), and secondly, many people do not implement this stuff correctly, so what you're usually looking at is something that looks like it might have been intended as a button, but is missing a bunch of features. Was that intentional or did the author just forget some stuff?
This isn't really a "pros and cons" type of thing. Just use a button. It's doing all the work that you'd have to do manually, but automatically. Just use it.
i would have imagined that coding html should be done by people with basic understanding of it, at the bare minumum to know what a button is. but maybe that's just me being old and not going on with the vibes ... maybe i'm just going to facepalm a bit and get some fresh air.
Before IE became Edge (and maybe even in the earliest versions of Edge), there were certain styles and descendants that simply did not work on a <button> element, like Flexbox and Grid positioning. So, if your button had content like an icon, and you were trying to align it a certain way with the label, you simply couldn't use some features of CSS like you could with a <div>. It was a pain in the ass.
In the same vein, do you remember the period where some browsers wouldn't allow you to make a button look like a link using CSS, because they thought it might deceive people and thus be a security issue? I do.
And similarly when people complain about the complexities of webpack and bundlers in general, do you remember including the jQuery <script> tag on the page and then almost always needing to call `jQuery.noConflict()`? And how in those days, most people got even THAT wrong, because atomic <script async onload> behavior didn't work correctly in all browsers yet, so other code could actually run in between a <script> and its onload callback, meaning the jQuery.noConflict call was ineffective and something else could steal it? I remember. webpack fixed that by automatically scoping everything.
Nowadays, a lot of those workarounds are unnecessary (depends what browsers you're supporting). But it's not like there was never a reason for them.
When comparing to a Button, that's a bug, not a feature.
It is INFINITELY easier to add type="button" than all of the other shit I mentioned in my article.
Another good example is bizarre error handling conventions when working in TypeScript. Claude will come up with tons of weird ways of doing this, using different approaches all over the place, but rarely consider simple patterns like 'return an expected value or an error'.
A button of default type will do weird things, IIRC it will skip the JS handler onCick, for instance.
You mention using it to skip a nav header or something similar, but (1) you have autofocus for that if you need it (and you don't usually need it), and (2) the pattern where you provide a "jump to content" button is typically a better approach, because it allows the user to decide what they want to do when they arrive on your site, rather than you deciding for them. If the "jump to content" button behind visible when focused and is the first element on the screen, then it works perfectly for screen readers and other keyboard users, and you don't need to handle taborder manually.
There are always exceptions to these sorts of rules, and some times tabindex might be necessary, but I've not yet come across a case where the problem couldn't be solved better in a different way.
Stop implementing date pickers when <input type="date"> exists.
Stop implementing smooth scrolling. Browsers already do it on their own, and your implementation will not work. Really, just don't mess with scrolling in general. Don't make scrolling have "momentum". Don't change scroll speed. One site I've been to goes out of its way to change how much a scroll wheel click scrolls the page. For fuck's sake, can someone explain to me why that would be a feature!? Why go out of your way to override a specific user preference!?
All this bullshit changes expected behaviors, reduces accessibility, reduces the performance of your web page (and therefore increases CPU and battery usage)...for no reason whatsoever.
Why does a website that sells to a pretty much captive audience who cares more about functionality than looks obsess so much about every single button looking pixel perfect to some arbitrary wireframe, I will never know.
And yes, this is an acceptable solution according to the W3C:
https://www.w3.org/WAI/WCAG21/Techniques/general/G1
Your solution of focusing the first form element is pretty idiosyncratic. It's better to follow WAI patterns, because patterns have predictable behavior. Otherwise, keyboard users will have to learn how to interact with your website from scratch, instead of just following the same pattern they're used to from other sites.
That person should sit alone in a room with no distractions and think about what they did.
10 years ago someone decided that dragging links is so much more important than selecting text, selecting text is scarcely possible. I'm going to have to fork a browser to give link-dragging the demotion it deserves. It was probably those DIV guys.
And here i am, wishing some would do just this. Especially creators of log in forms which make the “reveal password” button the next focus point instead of the “submit” button
Here's a tip for weirdo front-end devs that do this: You are not smarter than the people that created the spec.
This still has issues in some browsers. With sites already having other methods from before this existed, there isn’t a good reason to move to it, when they’ll need the custom version for browsers that lack full support.
This is the issue with a lot of newer features.
This piece of advice, to me, just feels like a piece of advice constantly repeated by a bunch of people, none of whom actually use the software for which the piece of advice is meant to benefit. That scares me; like we've all lost touch with the ground truth on this one; I'd love to re-sync with it, that's what I'm trying to do, I just don't have the first clue how to do it.
Then I write some basic CSS and show them they have nothing to fear.
Yet, I still remain irritated beyond belief that its such a common thing. In 2025. Hell, in 2017!
I don't know what to do about it, other than constantly remind people about things, but it gets tiring.
Though, its a great interview question. Its a quick way to understand if someone knows the fundamentals or not.
Kinda like how people got ".bind" wrong on functions for years.
[0] https://necolas.github.io/normalize.css/
[1] https://meyerweb.com/eric/tools/css/reset/
Unless the site is _really_ messing with events, holding alt on PC (Windows/Linux/ect) or Option on macOS lets you select text in links without triggering the link to navigate.
Your original comment was to use tabindex to skip search, menu bars, breadcrumbs, etc. and for that there are better options.
If you aren't expecting this (and don't know how to discover it e.g. by examining browser dev tools, server logs, etc.) then you'll assume the button is broken and... probably try something else.
Even if you do discover it, you may try something that won't quite have the same reliability - at one point it was common to see folks putting preventDefault() or return false in their click handlers to squelch the (correct) behavior, rather than changing the type of button.
- It only counts as "fucking with tabindex" if you give it a value that's not 0 or -1. You should give that specific disclaimer, because there are uses for tabindex=0 other than reimplementing <button>.
- Divs can definitely receive keydown events. If I go to an arbitrary web page, pick a div and run `div.tabIndex = 0;` + `div.addEventListener('keydown', console.log);`, I see those events coming through when I have the div keyboard-focused.
- "Run your code, somehow..." I think just calling `notRealBtn.click()` is the best option.
- Stupid but semi-interesting nitpick: 'keydown' is good for enter, but you should be listening to 'keyup' for the space bar. That's how real <button>s work anyway.
- The 'keyup' listener should call event.preventDefault() to prevent the default behavior of the space bar scrolling the page.
[1]: https://html.spec.whatwg.org/#the-button-element:concept-ele...
...unless someone made the phone # a tel: protocol link, in which case it has the selection behavior of any other link. Which is mostly fine, since "copy" is a context menu option for tel: links... unless some jerk put a tel: URL in that isn't the same number as what is shown in the text of the link, in which case it's time for some crazy hoop-jumping to either copy OR call the number.
Your knowledge is out of date. <input type="date"> been implemented in nearly every browser since 2018, with the exception of Safari which was slow and didn't put it in until 2021, and Opera Mini which still doesn't have it at all, but who the hell uses Opera Mini?
It’s strange to look back and see that most spa projects still just bundle it all up into a gigantic js file…
It might not be what the developer of the site/app intended; but it's exactly the semantics the user is expressing their desire to trigger. So why not do what the user wants? Browsers are user agents, not developer agents, after all.
(Before you say "that sounds like a layering violation" — well, yes it is, but that particular layering violation already exists to support most browsers' JS engines' "suppress popup-window / new-tab navigation if the stack of the navigating call doesn't contain a click event" logic. The code ugliness was already bought and paid for; we may as well reap as much benefit from it as we can!)
That’s not necessarily true. You could imagine writing perhaps a video game or something where this control is intended to have a different meaning. For this reason, I don’t think this is a liberty that browser developers can take.
And those devs set the wrong patterns and standards for others following hot behind them. The only time I can remember needing to dress a div up like a button was when an accordion trigger was just a giant button and anything passed in would be rendered inside, but I needed an action to the right of the trigger title. But those happen super rarely. You can't just pass in a button as it was invalid html to have nested buttons obviously. Yes, I know I could probably use css to absolutely position it or something but that takes it out of the flow and starts hacking about it in another way.
We had to invent our own buttons if we wanted it to look the same everywhere. I could be wrong though.
But that was in 2011–'13 and things have gotten a bit easier since then and it's up to us to stay abreast of what's possible on the platform.
> So you are jumping through all the other tabindex elements
This part is correct (for elements with an explicit positive tabindex), which is why specifying an explicit positive tabindex is considered a code smell. If you don’t specify a tabindex on an element that’s focusable by default, it behaves like tabindex=0.
Try it:
data:text/html,<button>foo</button><i tabindex=0>bar</i><button>baz</button>It's not "add a browser-default click event listener that shims in special behavior" (which wouldn't be able to catch the JS navigation in the first place, since that would be occurring in its own, separate click event listener.)
It's instead "when I-the-browser have been called by page JS to do something navigation-like through the native `location` or `history` APIs, first check if the current call stack has a event-listener call triggered by a browser-delivered click event in its ancestry. If it does, check further whether the click was a "special" [middle-mouse-button / shift / ctrl-or-cmd / alt] click. If it was, then cause some other side-effect depending on the "special" modifier [i.e. open the target URL in a new tab, or new window, or download it] while silently failing to actually navigate this tab. Otherwise, proceed with the original semantics of the native `location` / `history` API call that was made."
If your game isn't specifically trying to have middle-clicking / ctrl/cmd-clicking navigate the tab, then this check+logic would never fire.
---
That being said, in theory, if you bound a listener to `auxclick` and told it to `e.preventDefault()`, that should maybe tell the browser you're doing something special and different where the middle-mouse button isn't just "the left mouse button with extra flags set", and therefore that navigation triggered via the middle mouse button shouldn't be seen as "navigation triggered via the left mouse button with extra flags set."
I say in theory because web developers would probably use that to prevent people from opening their site/app in multiple tabs at once. And the whole point here is that people should be able to do this whether web developers like it or not.
--
Also, a tangent:
Web developers may have what are, to them, good reasons for preventing the user from opening their page/app in multiple tabs.
For instance, each instance of a page/app on their site might open its own persistent websocket connection to their backend, and so having O(N) tabs open to their site means O(N) websocket connections. And if many users do that, their backend falls over.
Or each instance of the page/app thinks it has exclusive ownership of a local IndexedDB, and so might try to migrate the data in a non-atomic way at any time; where if other instances of the page/app are also running and accessing the same DB, they might blow up, or do the same thing and corrupt the DB.
(I've seen both of these in practice. In fact, I believe Reddit [in its new design] does the former; at around ~100 open Reddit tabs, they all crash! [I think it has something to do with their chat system.])
IMHO, any webpage that can't stand being open multiple times simultaneously is badly architected. Such pages have been "coddled" too long by browser vendors, by allowing devs to mostly disable the user's ability to easily open links as tabs. We should stop coddling these pages.
If we give users back the ability to easily open tabs on modern websites, we'll see the owners of these sites finally [probably begrudgingly] fix the problems that cause them to want to disable opening things in tabs in the first place.
I can imagine screen readers would deal particularly poorly with this behaviour, because the user would be dropped in a field without any of the context of the page, just the field label to work with. However, I've not been able to test that out properly.
I don't think the behaviour you're describing is anywhere near as common as you think it is, and I suspect it would make a page less accessible for a number of kinds of users.
Click a link with left-mouse, and it'll intercept the page with their safety checker (that doesn't work half of the time) before forwarding you.
But middle click? No safety for you.
I find non-selectable text maddening, but I recently found an macOS app called TextSniper that restores my control. It lets you select an area with the mouse (as you would when taking a screenshot) and it then OCRs the text and puts it into your clipboard. It almost makes Google Analytics usable again.
I'm sure I'll trigger a lot of designers by saying this, but I'm probably not alone in valuing basic usability FAR above styling. I much rather have an ugly button that looks like 90's era Tcl/TK than something pretty that doesn't behave like I expect it to.
Yep, which guarantees you will not add everything that's required for e.g. accessibility. It's not realistic for every single dev to be aware of every single important property of a button. This approach just doesn't scale.
Which, don't get me wrong, is still a problem, accessibility matters, but if there's a reason as to why something happens, the way to fix it is to actually look at that reason.
I actively do not want you to even try to "make it look the same everywhere", because the way you want it to look will in general be a way that degrades functionality for me. When you try, you send the message that you know better than me about how I want the GUI on my computer to work; and that makes me less inclined to use your site, and thus your product.
When you're building a webpage or webapp, you're really building, like, 5% of an app. The other 95% is taken care of for you. You can always just say "fuck it, who cares if the button is gray in Firefox". Because you know what? Firefox might just fix it. And boom, you get the same result with no effort and no maintenance burden. And even if you don't, who cares? Maybe Firefox users like that and that's why they're on Firefox. All you need to know, as a web developer, is that the button does clicky things and is a button. That is a super power. We should use it!
I’d welcome people knowing a quarter of the html elements.
Because in my experience, it seems like people only know 1 html element, which is DIV and it’s used for everything.
Its modern replacement ASP .NET MVC works much more like traditional web frameworks.
But this is not a new problem or one "among the react crowd". Anecdotally I'd say it was more prevalent 10 or 15 years ago.
Click handlers however may stop working if there is even the smallest typo or error far away from the code.
It won't just be that one link. Everything stops working everywhere on the site.
I also can’t count on all my users to have the latest version of the browser, and don’t enjoy people messaging me telling my stuff doesn’t work.
Their intentions were good, but.. you don't know what devs are going to do. As long as they're following spec, let them. Users can vote with their wallet or eyeballs.
NB: I don't want to throw too much shade at Vivaldi. I reported it. They fixed it. Still my fav browser
This has especially been annoying when I have happened to use DuckDuckGo's image search instead of Google's or Bing's and instead of having opened ten tabs with links, I have ten tabs with a thumbnail in each ...
The compiler and preprocessors are very picky about accessibility issues like these.
It was annoying in the beginning, but I tried to follow the rules it set out. It feels very professional, and I’m thankful for the guidance.
What’s annoying now is when my coworkers thinks I’m making things unnecessarily complicated by trying to follow these rules and guidelines. A <div> with an onclick is so easy compared to a <button> with extra styling and sometimes calling preventDefault().
> 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>.
How does one's desire to use a framework have any impact on their ability to recognize the usefulness of a button? Did I miss something? Is there some portion of any of the major framework documenation that suggests it's better to use divs instead of buttons?
> Among the React crowd, and also among people who seem to enjoy HTMX, I see a lot this…
Lol This has been a problem since way before React hit the scene. And people not using frameworks aren't magically immune to using divs instead of buttons.
Maybe they're a younger developer and weren't around pre-React, but the idea that using a div instead of a button for a clickable element is somehow new or only pertinent if you're using a framework makes me think the author is confused about these things work.
https://www.w3.org/TR/html4/interact/forms.html#adef-type-BU...
I'm really struggling to understand how the author connected using frameworks with using divs for clickable elements. And yes, this problem predated React and HTMX.
One of my biggest pet peeves is throwing shade at a certain tech or group of people who use that tech and the proceeding to demonstrate that they don't know anything about said tech.
To me, it's a great example how LLMs are just as shit at coding as everything else. It's just that the majority of people using them to code don't look at how terrible it is as long as it runs.
People don't do stuff poorly on purpose. There's also nobody out there for which button elements are some arcane and obscure thing, we all know they exist, so what is happening?
I'm not arguing for using divs instead of buttons, I'm arguing that if that happens there's a reason for it, and we ought to look at that reason and attempt to deal with it rather than admonishing developers in general.
Would it be nice if this wasn’t necessary? Sure. But browsers aren’t going to break compatibility to fix this quirk.
> we ought to look at that reason and attempt to deal with it
I’m curious how you think we could deal with this aside from education.