Will browsers ever catch up here so that the right way to do it is the default way? Or at least one of the easiest ways?
It's clearly difficult if there's so many options and this generates so much discussion. It shouldn't be this hard to get a basic UI pattern right (submitting a form once with progress updates).
If it's even a little hard, it's inevitable 90% of sites will get it wrong. There's just too much stuff for devs to realistically stay on top of to blame them.
Related: the `inert` attribute has good support now for preventing users from editing/interacting with forms that are currently being submitted so you don't have to mess with overriding input events (https://developer.mozilla.org/en-US/docs/Web/HTML/Global_att...). You'll have to add it manually when the form is submitted though, remove it if the submission fails, and use something like aria-live to give progress updates (https://developer.mozilla.org/en-US/docs/Web/Accessibility/A...). You'll also need to manually test all your custom forms like this work on actual screen readers (they have inconsistent behaviour, similar to cross-browser bugs), as well as edge cases like resubmitting after a failure, when the network request fails with error feedback, and when the network request fails from a time out. Not easy.
`inert` has the exact same problem, and is arguably even worse since it removes content from the accessibility tree entirely:
> Specifically, inert does the following:
> Prevents the click event from being fired when the user clicks on the element.
> Prevents the focus event from being raised by preventing the element from gaining focus.
> Hides the element and its content from assistive technologies by excluding them from the accessibility tree.
An `inert` attribute is basically invisible to screenreaders, and if you have mitigations in place for that, you might as well just use `disabled`. So I advise against using `inert`, I'm not going to say it's completely useless, but I don't think there are ton of instances where it makes sense and it's easy to get wrong.
----
The real solution here is pretty obvious: `disabled` shouldn't block focus. It's not what users want in most situations, and there are easy ways to block focus from an element if it needs focus blocked. The problem is that it would probably be extremely complicated to change browser specs to implement the better behavior. Introducing another attribute that has the correct behavior would likely be easier, but it's still not going to be trivial to get that through standardization process.
But it's no great difficulty to block a button from being focusable in HTML, that's easy. We're only in this position because `disabled` blocks focus by default and offers no way to get focus back. Even manually setting a `tabindex` does nothing. And that's just bad design, clearly there are situations where disabled buttons should be able to receive focus. If not the default behavior, it should at least be possible to allow focus as an override.
Seems like the best option may be to add the disabled attribute to the button, as well as provide an aria-live section with a message stating that the form is in the process of submittal. Of course then you'll have to handle removing the disabled attribute, as well as providing a message either stating the submittal was successful, or that an error occurred.
I get that HTML wasn't supposed to be an application platform, but it's what we've got for now, and all we can do is work with what we have and keep pushing forwards for better standards or a better application delivery method (which is tough, since deployment as HTML is just so easy as compared to updating a rich client application).
To clarify, I only meant `inert` is another (fairly new) tool that can be useful, not a complete solution. For example, you can use it to prevent the user editing the fields of the form while the current field values are being submitted. You don't need to use it on the submit button.
As far as I remember, for similar behaviour you either have to add `disabled` to all the fields (then remember to revert only the `disabled` attributes you added if the form submit fails...) or wrap all the fields in a `disabled` `fieldset` tag (now you have to research if there's any quirks when you nest `fieldset`s).
Saying that, why keep the focus on the submit button? Are there no better alternatives?
> Saying that, why keep the focus on the submit button? Are there no better alternatives?
That is a good question. Probably not? I could be convinced otherwise, but I'm doubtful there's an obvious universal answer. If the form has an error, I suppose we can jump to the error field. If the form is successful, maybe we remove it from the DOM entirely? But both of those are situational and I don't feel confident that there's a universal answer.
And my instinct is (I might be wrong on this) that moving focus around automatically may just be disorienting to blind/low-vision users anyway and we should be cautious about doing so.
The current situation is I think the worst of all worlds. If a developer has the presence of mind to move the focus to another element, great. But if they don't think to do so, currently `disabled` just kind of dumps the focus, and that really feels like the worst outcome. To me, graceful degradation would be "we're going to keep you focused on the element you've selected unless the developer takes active steps to move your focus elsewhere."
The accessibility shouldn't completely fall over if the developer makes an accessibility mistake, there should be serviceable defaults for pages where developers put no extra effort into accessibility.
----
And even if we can figure out a universal standard for form behavior, buttons are used outside of forms, and `disabled` affects them there as well. If we want an accessible standard for where to move focus when a form submits, that should be a browser event that gets triggered off of the form submit event. Then it would be properly overridable with `preventDefault`, it would respond correctly to submit events even if the form button is left enabled.
It shouldn't be a result of the disabled buttons being unselectable, if it's a form-specific behavior it should be `form` behavior, not `button` behavior.
----
> To clarify, I only meant `inert` is another (fairly new) tool that can be useful, not a complete solution. For example, you can use it to prevent the user editing the fields of the form while the current field values are being submitted. You don't need to use it on the submit button.
Fair, but I personally would advise against this (although I'm open to other feedback if people have it) -- I don't think removing the form from the accessibility tree is a good response to an in-progress submit. I often like to compare with the visual UX when thinking about low-vision UX. If removing the form is the best option, then why wouldn't we full-on remove the form by setting it to `display: none`? I think the reason is that we recognize that hiding content from the page in response to an in-progress submission and then reintroducing that content based on error/success messages is bad visual UX.
So my feeling is that if it's important for a sighted user to be able to still see the form, then in most cases it's also probably important for a low-vision user to be able to access the form contents (but not edit them).
Remember that screenreaders and keyboard controls will respect `display: none`. So the only time we're using `inert` is for situations where we want content to be invisible to screenreaders but not to sighted users. And I just think that's a situation we should be very cautious about, typically the only time I'm trying to hide content from screenreaders is things like icon elements, tiny elements that shouldn't be read aloud, and usually I still want those elements to have some kind of label or reference anyway.
> If the form has an error, I suppose we can jump to the error field.
Yep, that's standard good practice I think for all users.
> If removing the form is the best option, then why wouldn't we full-on remove the form by setting it to `display: none`? I think the reason is that we recognize that hiding content from the page in response to an in-progress submission and then reintroducing that content based on error/success messages is bad visual UX.
So a common pattern right now to indicate the form is being submitted and it's going to take a moment is to fade out the form or the entire screen a little. And if it's going to take a few seconds, show a spinner, progress bar or progress message as well. `display: none` would look very jarring like the page just crashed. On a screen reader, you would want to announce the submission happened and the live progress, which you could get with little effort by wrapping the visual progress indicator with `aria-live`.
I agree to be cautious here but I'm not seeing a problem. I'm also not seeing the reason behind keeping the focus on the submit button when you can't interact with it now.
> I'm also not seeing the reason behind keeping the focus on the submit button when you can't interact with it now.
So let me complicate this a little bit because we're not talking about an immediate focus-change. It's not that a user clicks submit and immediately they should jump to the error field, there is a period of time where the form is being submitted and that's the only thing happening.
The question is, where should the focus go during that period?
Currently, if you do error-focusing then unless you also program handling for the progress event (which we'll get to below) what happens is the focus is on the button, then it's on the document, then it's back to the error. What I'm suggesting is that's pretty jarring, and it seems like the focus really should stay on the button until the developer is ready to move it elsewhere.
And you can move the focus anywhere else you want whenever you want, that's already easy to do, but if you forget to do so and you forget to account for the focus change, then dropping the user focus and throwing it onto the document is a really bad failure state.
And even if you do have progress-bar handling, for example in your fadeout example, if an immediate removal of the form is jarring to a visual user and they need a transition to make it more obvious what happened, then I would argue the same is true for a visually-challenged user.
While your form is fading out, maybe add an aria-live message that says submission is in progress, and by the time that message has finished being read out to a screenreader, the form will probably be finished fading and then you can move the focus wherever you want (assuming there is a good place to move it) and just remove the form from the DOM normally or set it to invisible in CSS, no extra attributes needed. This ends up being basically the same outcome as what you suggest, but I don't like this idea that we're going to kick focus to the document while we're waiting to put it somewhere else, and I don't like this idea that the browser is essentially saying "unless you have somewhere else better in mind to put the focus, then tough luck there is no focus anymore."
I buy that you might have situations where it makes sense to move the focus. I don't buy that outside of those situations the best default behavior for a browser is to drop the focus entirely and move it to the document. And while I'm more open to the idea, I also still sort of don't buy that situations that require visual transitions to improve UX for sighted users are good examples of when we should move focus immediately before giving a non-sighted user any feedback or heads up of what's happening.
----
As a side note, I do want to re-emphasize again as well, buttons are used outside of forms. If we want forms to push focus to the first errored field by default whenever a submission finishes, that really ideally would be default behavior for the `form` tag, not for buttons.
Form behavior should really not be dictating how focus events get handled for disabled buttons. The question is whether there's ever a use-case for focusing a disabled button (in or outside of a form) because right now it's impossible to do so. We can always get rid of focus from a button, but we can't put it back if the browser prevents focus entirely, so unless we're completely certain that we're never going to need to focus a disabled button, I don't think it makes sense to design an attribute in a way that forces that behavior.
`inert` is intended primarily to help with overlay focusing (think dialogs, drawers etc) or offscreen elements that should be hidden. While yes, it can be added to any element (and therefore make it and its subtree inert) which could enable some kind of novel use case, I think the standard case are primarily dialogs / overlays and offscreen elements that need to be hidden
I'm open to a use-case here that isn't obvious to me, I'm not saying you're wrong. But I don't understand why I wouldn't use `display: none` for an offscreen element or a hidden dialog/overlay? Screenreaders do skip over `display: none`, right?
If I'm moving an element offscreen but not full-on hiding it, in my experience 99% of the time it's because I want it to be visually hidden but still show up for a screenreader. `inert` gives me the opposite behavior by default. I'm trying to think when I've ever wanted an element to be visually hidden from both sighted users and a screenreader, but also couldn't do that via normal CSS.
Again, open to learning more, but this still seems to me like an attribute that should be generally avoided. I can think of some use-cases; maybe I have something like a chart where there are a lot of focusable elements but where the experience is awful for keyboard or screenreaders and I want to basically declare bankruptcy and say, "no, I don't want it to work with those controls at all, it might as well not exist for those people."
fragment content that be moved to be visible but you don't want announced. Things like long form news articles where you might render the graphics with accompanying descriptions off screen (to save cycles) but as you scroll into view while reading you shift it to be visible.
Really its good for anything where you don't want the browser to "paint" but you want to have the content fully loaded without the user having any idea until you essentially want them to
> Will browsers ever catch up here so that the right way to do it is the default way? Or at least one of the easiest ways?
My pet peevee is how browsers; and later smartphones; normalized that interactive elements can reflow/reorganize all the time. Now, things like joining a specific WiFi, or mirropring to a specific TV, can become a game of chance if you don't let the UI settle a bit. We had this problem more or less solved before.
You're absolutely right. It took me years to spoon feed myself web development while on the clock. I don't think there's a proper path forward for learning HTML and everyone has different opinions. But learning about WCAG should come right after learning HTML and CSS. Study BEM and a CSS library. Then basic Javascript or framework basics follow by CSS and JS molecular design principals with JSON and fetching. Then apply HTML semantics to write user actor tests.
Anything I missed it's because I still haven't learned it so yes, it's a crazy world.
If accessibility tools remove the focus from the disabled button and focus the document instead when a button is disabled, wouldn't this be a problem of the accessibility tool in the first place?
Even just focussing the parent element would be a much much better idea than focusing the document.
Making a button look disabled without actuall disabling it sounds like a semantically bad idea.
Isn't it instead that the browser changes the focus target upon disablement, and the accessibility tool merely follows? That's what I understood but I may be wrong.
Basically the whole "disable submit button after submit" thing is a lazy hack.
Double submit should be prevented by other means and "disabled" is semantically different from "this action is in progress".
This problem can basically only occur for async form submits, so one can easily just alter the button and replace it with a dummy like ("submitting") in any way it seems fit, using JS.
One can also set a "submitting" CSS class for the UI state and change text accordingly (or aria-label).
For old-fashioned plain HTML form submits, the browser handles the client side of the double-submit problem already, no?
I believe it is solvable without JS - you can’t prevent the request from being created, but you can prevent the second submission for having any effect.
You could add a hidden UUID to the form server side and only process any submission with a given UUID once. I believe something similar is commonly done for anti-CSRF purposes.
Alternatively, just rate limit users. A user making multiple comments within a few seconds of each other is probably either accidentally double-submitting or spamming. I think this is what HN does.
> You could add a hidden UUID to the form server side and only process any submission with a given UUID once.
Not if you have a load balancer that might send the two requests to different servers. If all of your requests are idempotent (difficult to pull off, but often worth it), you might be able to just let both servers handle it. But you still have your users inadvertently hogging resources they don't need. And if your request is taking a long time, a pissed-off user could be spamming the submit button in frustration (I'm sure I've done this before).
You should absolutely prevent the 2nd submit in JavaScript, but I'll accept that disabling the button is not the best approach for that.
> I believe something similar is commonly done for anti-CSRF purposes.
That's a crypto-signed token you can use to verify you've pre-authorized the submission, not just some UUID. (You could do it with UUIDs if you keep a list, but ... don't)
> That's a crypto-signed token you can use to verify you've pre-authorized the submission, not just some UUID. (You could do it with UUIDs if you keep a list, but ... don't)
I'm struggling to remember the exact details here, but I believe Drupal 7 does what I described and just keeps a big list (expiry on entries set to 6 hours) in their database. I remember having issues with it getting very large when we had issues with bad scrapers hammering our site. Part of the issue though is that Drupal tracks the entire form (in order to prevent form tampering issues) and not just the UUID, which makes the table much larger than necessary for the use case of preventing CSRF and double-submits.
it's not a lazy hack, but a useful indication to the user that his action has an effect. Besides double submit there is also an issue of a single sumbit - have I actually clicked on the button or somewhere nearby or never properly clicked or has something else, pardon the pun, not clicked?
You imply that this is the only possibility to provide user indication for a form submit - why?
What about some stupid text below the button: "Your data is being submitted..."?
Seems the meat of the argument is the disabling behavior, which is really trivial to implement in JS.
When I want to submit a form and the button changes to a regular grayed-out disabled button on click, on top of that without a page reload, I'd assume something went wrong.
You need to distinguish "disabled" from "action in progress" any way, by using a bit of special casing and CSS polish for the "submitting disabled" button... in which case, why use the disabled attribute at all, instead of preventing submit in JS?
It's not harder compared to toggling attribute in JS.
I don't imply that, you can add some text, it would still not make the original fundamental UI feedback benefit disappear and make it a lazy hack. The text is ok as an addition, but worse as a substitute - the feedback should be a property of the UI element you interact with.
> , by using a bit of special casing and CSS polish for the "submitting disabled" button..
or by simply adding a different label to the now disabled button?
> why use the disabled attribute at all
to get the same outcome in a simpler manner? But if you recreate all the pieces with in an alternative way, that's fine, don't toggle the attribute
EDIT: My reply below is wrong - you _can_ deal with in-flight requests with E-Tags as well using the if-match header. I had only seen them used with if-none-match. TIL.
E-Tags would probably not help here, because the client won't have a tag to send along with its request until after the original response returns. But the thing we're trying to prevent in the first place is multiple concurrent requests being processed.
The Idempotency-Key header[1] is probably a better fit, but that relies on the server implementing the spec, including properly dealing with multiple in-flight requests with the same header. I found this blog post[2] pretty good for exploring some of the implementation challenges.
A double click is something people do within a split second. Within a few ms, your application is now trying let's say, to store a comment and send a notification. Even with some sort of one-time token, process A will check the token, mark it 'used', but process B may be doing the same thing, and when process B started, the token was valid then too. If stored in SQL, you can try to do something with locking the table or row, and a second process might not get to update (or you can look in to optimistic locking/versioning/etc) but it's a thorny problem to deal with when people 'double click' and two processes start within a few ms of each other.
tldr: disabling a submit button to prevent a double click scenario is by far the easiest and most pragmatic approach that stops the problem from entering "much more complex/difficult" territory. Ideally, the 'disabled' state might auto-undisable after a couple seconds, but the original request handler should be able to reset the state after processing the response.
Would be so much nicer if this was a standard attribute to add in to forms to prevent this (2 single clicks registered back to back within X ms... ignore second).
edit: finished the article, and the 'data-submitting' approach is something I also have implemented some times, and works decently well as an alternative. You're still having to prevent double submission. etags won't really help there.
Redis should be able to handle tens of thousands of reads/writes without much issue on contemporary hardware.
Every request that comes in contains a random "idempotency token" (can be in a hidden form field) and upon reception the server checks the previous value & sets the key if it's not there (GETSET command on Redis should do it?)
The presence of the previous value indicates whether this token was handled already (if so it means duplicate request and return an error/etc), and setting the value will ensure any subsequent duplicate request will get rejected.
> "disabled" is semantically different from "this action is in progress"
Different, but not contradictory. Disabling just means "user can't do that", which is semantically reasonable when you don't want the user to do that. That stays true when the ultimate cause is that they already did that.
Yeah that's true, I was writing this from my lunch break in a hurry and this was sloppy/wrong.
What I have actually opted for so far in SPAs and other JS form submits is to show a loading indicator inside the button tag, and ignoring the function call as long as a request is pending.
Sibling comments and the article suggest equivalent solutions.
Re sync form submit:
OK, it is still possible to trigger double submits, youre right.
I'd argue that 99% of the users notice their browser's native loading indicator though. Also I have personally experienced the "do you want to submit this POST request again?" popup for spamming form submit buttons without manually reloading the page, but I'm not sure how reliable that is or what the preconditions are.
This being possible is directly caused by browser trying to appear faster by not replacing the current page until they can start rendering the next one. Like with bfcache, vendors do their best to make full page reloads feel like an SPA if the site performance allows.
Which brings us to the only sane answer: if you need to reliably prevent double submits against malicious or clueless users, you need to act on the server.
You can't de-duplicate HTTP requests with client code.
I have been working on the web for 20 years, which isn't much but it's long enough to notice a pattern.
1. Accessibility tool handles common things badly
2. Accessibility nerds try (and fail) to get everyone to stop doing common thing
3. Accessibility tool handles common thing correctly
There is a lot of great stuff you can do for accessibility, but always ask yourself if it is adding more data (like aria labels) or avoiding something you legitimately understand is not going to be compatible with accessible browsing (like onClick components inside onClick components).
But things like "don't use basic HTML functionality because tools are bad" is advice that will look very silly in 5 years when the cycle continues.
I guess one more is to be very careful not to assume anything is good or bad for accessibility. Most of the people talking have no experience with it whatsoever, and have not even installed the most popular software.
It is extremely complicated for browsers to change their existing behavior, and we should be cautious about them doing so.
We could however introduce another attribute that has the correct behavior, deprecate `disabled`, and throw warnings during ARIA validation/linting for pages that continue to use it. Call it `blocked` or `unsusable` or have if you want to be fancy, make a `status` or something with attribute with multiple values that it accepts. Whatever seems most reasonable.
But my point is we're not trapped in the world of developers needing to poorly replicate browser functionality for every single form they make; we could still have an attribute that makes it easy for developers to by-default program forms with the correct behavior.
In Javascript, adding `let` and `const` didn't require us to get rid of `var`. We didn't have to change `var` behavior to make `let` throw errors on redeclarations. There are options here for providing tools within browsers that work correctly.
They can, but they have to think a lot about backwards compatibility and existing code. So sometimes they can't, and in this particular case I think it's going to be hard, mostly because of existing Javascript code.
Also there's standardization and coordination between browsers, but if there is a genuine need for something they tend to find a way.
Even if the browser is somehow required to move the official "focus" to an arbitrary useless location, they don't have to treat that focus as the sole gospel truth of where to read from.
No, they literally have to. The accessibility tool landscape is a wild place, since there are so many different human disability types, and people need different assistance in different ways, so there is no one tool that can help everyone end-to-end. Some people can see, but their hands can't type, so they use a custom input method. Some people can't see, but they can type, so they just need to be able to hear the page but otherwise can interact with the page with their normal keyboard. Some people have issues both seeing, hearing, and typing, so they have their own custom setup.
The browser's focus has to be taken as the gospel truth, because that is the only thing that all these different assistance tools can agree on. There is no standard otherwise. If you want to be a smartass and develop a speech-to-text tool that does it's own focus management separate from the browser's, well now people who need custom input solution can't use your tool, because what they are typing into (browser's focus) is different from what your tool is focusing on.
No one is inputting directly to window.document, so in the scenario in the article you need to move the browser focus separately anyway. You'll notice I specifically said "where to read from", which is a distinctly different question from where input goes. Yes, you'll want to bring them together pretty often, but that's still better than dumping the user at the document root anytime a developer does something normal and predictable like disabling a button.
It virtually always better to allow the button to be pressed and present a reason why the click action failed. "this field is required" or "this field is not valid" or "do this action first". A disabled button is just confusing even for people that can see.
Of course the submission needs to be handled separately (if a <form> tag is being used). But that's not the point. The semantics for a disabled button are much clearer: You indicate that the button cannot be pressed (because it wouldn't actually do anything). Having a button that can be clicked, but that doesn't do anything, sounds like a wrong choice. Even worse is "making it look like it cannot be pressed by adding CSS rules".
From what I know about accessibility, semantics are key. If the semantics are clear, then accessibility tools can optimize their flows accordingly. Using styling to convey semantics is a bad idea.
If accessibility tools cannot deal well with certain situations, then those tools should be improved instead of making semantics worse.
I agree that is a strange article, setting disabled on the submit button is absolutely the correct semantic solution, the CSS presented as a workaround is also not enough, you'll need to set CSS for pointer-events, hover, active, and focus at least to re-create (badly) what the browser actually does when a button is disabled. That feels like such a hack. Of course you need to block the onSubmit event on the form element too but that is a separate issue entirely.
Do we really now have to work around broken screen readers too? Fuck that.
Well, the article is right, though. The disabled attribute resets focus to the document which is really bad UX.
It might seem like a trivial detail, but it's not for people who depend on keyboard navigation nor is it for any form where users might want to keep the button focused after submit. You don't have to use a screen reader to be inconvenienced here.
I've had to handle the latter case in game-related apps, but another example off the top of my head would be the obvious ability to resubmit the Reddit reply form by hitting Enter again when it responds with its "Error, try again" message. I don't see the argument for settling for the poor UX of using the disabled attribute here if you knew better and had the time/energy to polish it.
There is a level of polish where it makes sense to replace the disabled attribute with a disabled (e.g. "submitting") class which implements all the things you expect, like pointer-event changes, but is even more powerful, like being able to use it on a wider variety of elements and even container elements like `form`.
Finally, as the article points out, you still to write JS to prevent double submits even when using the disabled attribute. It's used as a quick hack rather than any sort of "submitting" semantics, so there's nothing lost recreating it with CSS.
> Finally, as the article points out, you still to write JS to prevent double submits even when using the disabled attribute. It's used as a quick hack rather than any sort of "submitting" semantics, so there's nothing lost recreating it with CSS.
Eh, disabling an entire form submit is easy.
Developers who manually replicate built-in browser features though and manually duplicate those features through CSS have far more opportunities to make mistakes and make inaccessible forms that don't account for keyboard focus vs click focus, different input methods, etc...
The article is correct in the sense that it doesn't matter what the correct behavior is, if you want to build an accessible site for browsers as they exist today then you have to care about this. But it's still the fault of the browsers if developers are working around browser quirks because the built-in semantics that the browser exposes provide a bad UX. Ideally, over time, those quirks should be fixed within browsers, although of course doing so on the web is very complicated.
Would it be a good idea to first move the focus to an adjacent item before disabling the submit button? Like, have something that says "Submitting...", move the focus to that, disable the [Submit] button, and also tag the form element with an attribute that marks it as already sent.
1. The screenreader transforms the visual structure into something understandable for the human behind it
2. The human tries to make sense of it and maintain a mental model of what the screenreader is presenting to them
Semantics are key for #1, but #2 can still lose track of what's happening on a complex form. For that, it helps if the human can keep trying, and fix whatever they missed on the next pass.
People here are arguing about whether a form being submitted counts as actually disabled or not, but that's missing the broader point: even clearly disabled buttons should be selectable, because they might focus tooltips showing why they're disabled, or because users using a keyboard cycling through the interface shouldn't have their muscle memory interrupted by having the number of tabs required to reach a button change unexpectedly depending on the context, or to prevent errors during automation and scripting, and on and on.
Even if you believe that an "in-progress" indicator should be treated differently from "disabled", it's still bad UX that disabled buttons aren't focusable and it's still bad semantics to avoid a browser attribute just to fix that problem.
Now, you might have to avoid it anyway, because good luck getting browsers and screenreaders to all change their behavior; that would be fairly difficult to do. But... it is their fault regardless of what the rest of us on the web have to do to cover for their mistake and to make our applications accessible to people who are using these tools with their broken behaviors.
> When needing to disable native HTML form controls, developers will need to specify the disabled attribute, as it provides all of the generally expected features of disabling a control by default. However, there can be instances where elements need to be exposed as disabled, but are still available for users to find when navigating via the Tab key. Doing so can improve their discoverability as they will not be removed from the focus order of the web page, as aria-disabled does not change the focusability of such elements, nor will the elements be dimmed by default browser styling, making them easier to read. Some examples of where this may be useful include:
> The header button element associated with non-collapsible accordion panel,
> A button which is important to keep in the page's focus order, but its action is presently unavailable - such as submitting a form,
I would argue that if you are calling an aria-attribute and ignoring the real attribute specifically because the actual attribute is stricter than the aria-attribute and has unintended side-effects, then maybe the actual attribute is not accurately representing its semantic value or its expected semantic behavior.
If screenreaders and browsers aren't using aria-disabled as an indicator to skip focus, then the clear indication from that is that disabled elements should not necessarily skip focus. If disabled elements were meant to universally be unfocusable, then aria-disabled would block focus.
Maybe pedantic, but OPs advise is actually "disable the button, but don't use the disable attribute to do it". Instead they store the disabled state in the form and set a CSS style to make the button look like a disabled button. Which makes that information unavailable to screen readers, but avoids screen readers jumping around the page because button loses focus when disabled.
Also a useful note that disabling the button with the disabled attribute isn't enough to prevent people from submitting a form. Which is useful to remember, but not actually a justification for any of this (you can just check the disabled state of the button in your submit method)
Frustratingly, there is also an aria attribute for this, aria-disabled, that does not remove focus, and it should absolutely be added to any button implementing this strategy.
If you're going to follow this article's advice, don't add a [data-submitting] attribute; use the aria-disabled as your selector. A custom data attribute does nothing for screenreaders or blind users who have no idea now whether a given button is disabled or not.
OP's advice is also incredibly frustrating if you've ever encountered a form where the Submit button seems to just do nothing. (Yes, you could pop a dialog which tells the user why it's doing nothing. In practice, that doesn't always occur.)
I'm thinking a loading spinner would be the best compromise? Notifies the user that their input has been registered, and also explains why clicking again won't have any effect?
So really all this whole post is saying is that the browser behaviour of an attribute-disabled button isn't optimal and that we can do better for accessibility, etc. Way to bury the lede.
> What if you submit the form, but the request never arrives?
I think the proper way is to disable the button, then set the subsequent state of the button depending on the response from the server if any.
submit => check button state and if enabled then => disable button => server response (or not, then timeout) => update button state (along with error message if any)
That flow is exactly what this article is telling you not to do. Basically the article said "don't do X, do Y instead", and GP said "ok, but if I don't do X, what if Z happens?" and your response is "the proper way to do it is X". If that's true, can you address the reasons the article says to not do X?
I think the salient point of the article was not to use the disable attribute and instead to use CSS to indicate a disabled state. As I understood the article, they were not against disabling the click per se, but against using the disabled attribute because of its effect on screen readers.
GP by contrast was suggesting allowing someone to press submit multiple times. These are not the same discussion as I see it. My response was more to address that, and was agnostic about whether the attribute or CSS disables the button.
As for using the disabled attribute, I agree with comments that it's not semantic to simulate `disabled` with CSS when we have a perfectly good disabled attribute, and it's the responsibility in this case of the screen readers to render this situation as it is meant to be understood. I'm open to reasonable disagreement here but I think the consensus is that the article is incorrect.
As for allowing multiple submissions, I think it's probably a good idea in general to program defensively against duplicate submissions and return a 409 when in doubt. But the user experience is improved by not allowing duplicate submissions on the front end for several reasons, among them that it's confusing. A POST is not idempotent, but allowing multiple submissions treats it that way.
The article seems to discourage disabled buttons for accessibility reasons also.
The article uses the wording “re-enable the button after the API responds”, but doesn’t elaborate on failure/timeout.
There’s other stuff you may want to do, like persist form data. Or if you go with the article’s recommendation of preserving the button, but make it inert, perhaps add some indication to what the request is doing.
There is nothing as a “sufficiently smart” website that altered the browser’s visual language of page loading progress. Meetup.com is doing this to me lately; some links I just don’t know if they’re broken, or my net is being slow, and I’m being impatient.
Similarly, don't disable submit buttons so you can wait for me to type something. Especially for login forms, many password managers don't type usernames and passwords, but some login forms wait for you to type something before they'll allow you to submit.
Furthermore, these forms often don't notice when your password manager has filled in the fields for you, so you have to go back and make a meaningless edit (e.g. add and then remove a character) in order to enable to the submit button.
I assume they're in the group of users these sorts of interfaces are designed for. Hunt and peck typing, will dutifully following the flow chart of interaction across multiple pages and assume they are wrong when they are made to start again when they click a wrong button, and not being annoyed when the "you are being logged out in 30 seconds. Cancel?" message pops up.
Or, they are used to finding someone who's "good with computers" to assist. That alone probably hides so much garbage UX from being known to developers.
I've watched my parents use phones. The main things that throw them off are borderless buttons, long-press actions, and swiping/dragging table rows. If we could all agree to stop implementing these that's be great.
I think it's better to let the user click the button and then have it tell them why the submission didn't work instead of just disabling the ability to submit. Put all the onus on figuring out what's specifically missing/invalid on the validation code, not the user.
Yes. But secure password managers should be registering a virtual keyboard and typing into the fields with it, instead of using the clipboard which can very easily lead to exposing your password.
Neither is necessary. Secure password managers should directly set the value attribute on the text fields (the password manager has direct access to the page's DOM), and then they should use a DispatchEvent to notify the page that the field has changed.
Websites, applications, and login forms should handle those generic events rather than listening specifically to keydown events.
There's really no need to simulate typing or to touch the clipboard if you're using a browser extension; and while I haven't checked, I'd honestly be kind of surprised if any of the major password manager extensions are doing so.
Of course, if for some reason the user decides to copy and paste their password, then a change event should also be handled, not just a keydown event.
Keepassxc has an auto-type command that does type in the username/password into your "last viewed window". This is for cases when you don't have a browser extension or want to put in a password into the terminal, or some electron app.
That's a good point, although I would argue that's not what most users are going to run into, and I would note that properly handling a `DispatchEvent` and listening for changes will also properly handle KeepassXC's situation.
My main point is that it's not a security issue that browser extensions don't simulate a keyboard. I'll grant that some applications need to simulate a keyboard because they don't have any access to the page or application, and yeah, they have a good reason for what they're doing, but it's a rare situation.
And it is technically generally not ideal for a password manager to be doing that for websites; it does tend to make the user more vulnerable to phishing attacks. But it's fine, I see the use-case, I don't think KeepassXC is wrong to include that feature or that users are wrong to use it.
It just shouldn't be impacting how login forms are designed or what events they listen to. The users who are complaining above about login forms not listening to the correct events are most likely not being insecure and are most likely not using a clipboard when they fill in their passwords, and if the password managers they're using within their browser swapped from DOM manipulation over to simulating a keyboard, that would make those password managers less secure. Simulating a keyboard is something a secure password manager should mostly avoid unless for some reason it doesn't have access to the DOM.
The article doesn't mention this, but if you are going to build your own disabled state, at the very least include an aria-disabled attribute on your button. See Mozilla docs on the subject, which mention this exact same scenario: https://developer.mozilla.org/en-US/docs/Web/Accessibility/A...
Using pure-CSS and data-attributes without additional indicators doesn't help low-vision users; the only thing they'll be able to tell is that click doesn't work on some of your buttons, they won't be able to see the CSS or non-semantic attributes that say the button is disabled. If you're getting rid of browser semantics, you need to include the relevant aria semantics to compensate.
The article is right, but it’s also an indictment of just how poorly designed HTML is for dynamic applications.
Preventing double submission of data is a thoroughly basic feature for any UI library. It should be easy and obvious. But for HTML forms, the properly accessible solution shown here involves a non-intuitive combination of JavaScript, CSS and custom data attributes on an element.
HTML is just the language for describing the page content. It's not designed to be dynamic. A modern JS component library could have support for something like this.
The author's suggestion of using a data attribute instead of adding a disabled class to the element and the styling of it is more of preference thing.
That said, I don't really understand why there isn't something like a "singleSubmit" attribute you can add to a HTML form to automatically disable duplicate submissions. Handling double form submits is a frequent problem – you almost never want a form to be immediately resubmitable which HTML forms are.
Forms are one of the rare instances where "dynamic" content was actually designed into HTML. Sure, the original intention was to have a page transition on form submission, but for the user that might be indistuinishable from what JavaScript is doing today.
A much better behavior would be to have the form handle the button state automatically: once you submit the form, all submit buttons of the form are disabled until the completion the button's onclick handler, the form's onsubmit handler, and any request triggered by the form action. Once all of these are done, all submit buttons belonging to this form are automatically enabled again.
If you want to get fancy, HTML could have introduced a fifth button state for this to make it distinct from a "normal" disabled button, and maybe allowed the form to change the button content for that state (you might want the button to say "please wait" or "loading"). But really, just simple sane handling of the disabled state would have been so much better than the barebones forms we have. Make it overridable by an attribute if you really want to support the edge case of a form having intentional concurrent submissions.
To clarify, I don't think HTML should be expected to be responsible for button styling – that is a CSS concern. But I was really more responding to the "why can't I do this with a HTML library" comment, which you can but with a JS library instead. And while it's true HTML has some dynamic elements the assertion that you should be able to build advanced dynamic behaviour like this in HTML alone I think misunderstands the purpose of HTML. JS was built for more advanced dynamic behaviour, and you can absolutely do what the parent commenter is asking for with a JS library.
To defend my suggestion of a "singleSubmit" attribute – I think the form element itself which already has the ability to do some basic dynamic stuff like submit a form in various ways could reasonably include the basic requirement of not allowing said form to be submitted twice. Personally this isn't something I think is a huge issue because most of the time when we'd want to do this we're looking to build fancy dynamic form behaviour anyway and in those cases JS is going to be what you need to use for that. But there are some simple webpages out there which don't care about any of that fancy stuff and literally just want a form on a page which POSTs data to another page, and in those cases it would be nice to have the "singleSubmit" attribute I suggested.
I didn't see this as cognitive dissonance. It is "HTML and CSS are declarative. It would be good if there were a way to specify this requirement declaratively."
This problem is bigger than JavaScript and dynamic applications. Even decades ago you'd see warnings to "please only click submit once", because most users don't look at the browser activity state to see if it is actually doing something (regardless of it being an spa or classic pages).
There needs to be two kinds of forms, one that asks if you really want to re-submit, and another that allows infinite submission. The backend should handle the problem too, but this would go a long way toward a better default UX.
> That said, I don't really understand why there isn't something like a "singleSubmit" attribute you can add to a HTML form to automatically disable duplicate submissions. Handling double form submits is a frequent problem – you almost never want a form to be immediately resubmit[t]able which HTML forms are.
I assume because submitting a form takes you to a different page, where the form no longer exists.
> I don't really understand why there isn't something like a "singleSubmit" attribute
addEventListener has a 'once' option that will only fire the event once. If you're willing to handle all the error cases, it seems like the easiest option.
Http Post was not designed to be Idempotent so, basically you either fix double submission or your post starts to be Idempotent, which is basically against the spec (it’s not directly against it, but put would be preferred in that case). Of course you can always add an uuid hidden field and do upsert instead of insert/update. But such things are way harder to do.
Had things gone differently you could tell your form to use a put. Not sure if that works in current browsers.
Either way you'd need to validate the input in the server side anyway. What are you going to do if the user (agent) misbehaves? Reddit frequently has this problem with comments popping up lots of times because of a shaky connection, and frankly I dislike letting data quality rely on your users behaving as you expect.
Even if it's handled in the backend, if you receive zero visual feedback from clicking the button, you'll just keep spamming it until something happens.
Not that great of an experience when you sit there and "nothing happens".
Disagree. If it is currently invalid to click the button, disable it, that is what it is for. Disabled buttons should be focusable, that resolves the issue with disabling the focused button in response to a click. How you achieve this in any given environment is a different issue, if the out of the box disabled functionality does not work properly, you will have to find workarounds.
So you have focus on a button, which then becomes disabled, this throws focus back at the document level.
That sounds like a browser bug to me. It's absurd behavior. The focus change is as unhelpful as is humanly possible. It's not what the developer intended, it's not what anyone possibly would intend, so why is it the default behavior?
Same for "implementing an ARIA live region"
How about no? Developers are supposed to piece together hundreds of obscure articles to figure out the right way to build an accessible form. Most won't, but if they do, they're still not confident about its implementation.
How about after 30 years the web platform provides some elements that actually do something, with sane defaults instead of millions of developers being puzzled what to do for even the most basic of tasks?
It's not a UX article, it's an accessibility article. Disabling the button can be confusing for users relying on screen readers, and if it's the only way the "submission pending" state is communicated to the user then it's plainly non-conforming with WCAG.
The browsers and/or the screen readers are the problem here. Resetting focus to the document level doesn't make sense, and screen readers should just be able to read disabled buttons.
The proposed solution (hacking together a pseudo-disabled button) will only lead to more ugly buttons that don't seem to do anything. Setting the transparency on a button does not make it look disabled, nor does it stop the click animation which would suggest the button is still activating something.
If you're going to hack around silly browser bugs like these, add a loading spinner with ARIA description "submitting your form" and put it into focus. That way you can disable buttons all you like without resorting to weird workarounds.
Seeing as most forms work this way...shouldn't the screen reader understand this? You are in a form, the button is the submit button. It went to disabled after form submit. Save that ref, fake the tab index so tabbing takes you to the next item, if the button becomes enabled again, highlight it. This feels like an easy win.
I go one step further with buttons in my own React UI library, and I wish all UI libraries did the same:
When a button is “disabled”, it should show why either on hover or on click.
It’s a common frustration in software, especially complex ones like Adobe Photoshop or Autodesk Fusion 360, where a button is disabled and you cannot figure out why. For example: you can’t fill because it’s a “smart layer” and you need to rasterise it. Or you can’t “join” because both CAD bodies are fixed.
The programs KNOW why the button is disabled — otherwise how would it know to disable the button! So show the user why!
This alone would save countless person-hours and reduce a lot of frustration.
Kinda harsh to see it that way. You could remove the last four 4 sentences of the post and don't see it as an ad at all. The post does actually explain the most important part and the guy just tells us we can hire him if we need help.
What information exactly are you missing? The post lists why developers do it, why it’s bad, and what you can do instead - as promised.
I don't think we should have an "ad" tag. Most "show hn" posts are basically ads for new products, and still very useful and very welcome. If you think a submission is inappropriate or dishonest, you can flag it and write your reasons in comments.
Stupid question - couldn't (shouldn't) form submission be sort of idempotent? That is, if the same thing gets submitted again, drop it on the server side, rather than doing all sorts of fancy stuff on the client?
Should I not be able to send money to the same person twice in a row? Unless you add a single-use token to the form, your server doesn't know whether it was a misclick or a legitimate action. The client more or less knows.
Yes it should be. But belt and suspenders. It’s crummy UX for it to happen in the first place. And an easy way to accidentally spam calls if someone’s cat sits on the enter key.
Thank you, I was scanning the replies looking for this. If preventing duplicate submissions is a concern, you must prevent it on the server side. You should also try to prevent it on the client for a better UX.
(It’s not written in the most obvious way as regards this detail, but it’s clearly there: if there is a default button, click on it, if you can—implied is: if you can’t, just do nothing; and if there isn’t a default button, try to just submit the form.)
If the problem is shifting focus to the document, is it not better to: before disabling the button, with the attribute, create an element that contains information about the action in progress and focus on this element via `element.focus();`, and then add the "disabled" attribute?
I agree, you're avoiding disabling the button to cater for screen readers, at that point go one step further and move to an (invisible?) "Form sent" element ..
For me, the more common use case is to prevent users from making unnecessary mistakes. Only enable elements (like buttons) when all prerequisites are fulfilled. Simple example: if you require a user-name and password to login, then only enable the login button when that information is present.
This improves the experience for the vast majority of users; it prevents them from overlooking something, and having to go back later and fix it. If this makes life difficult for a very few? That's probably a worthwhile tradeoff.
Anyway, if you actually care about the people using screen readers, you should be offering them a separate experience anyway. One that doesn't involve JavaScript. Bet: the author of the article hasn't ever tried to use his fancy web-forms in Lynx.
> For me, the more common use case is to prevent users from making unnecessary mistakes. Only enable elements (like buttons) when all prerequisites are fulfilled.
I’d argue this is actually worse UX, because in some cases (like a login form), it’s obvious, but in other cases, it might not be immediately clear to a user why the button is disabled. IMO it’s better UX to always enable the button, but provide clear messaging explaining 1) why the user couldn’t do the thing and 2) what needs to be fixed in order to do the thing
> Anyway, if you actually care about the people using screen readers, you should be offering them a separate experience anyway.
That's a very odd claim. Pretty much nobody does that, and segregating people is not generally a good idea. You need to bake accessibility into everything you do. Screen readers and javascript work well enough together.
It's an excellent point but perhaps the solution still doesn't solve the problems. Like, it fixes the problem for accessibility but then creates or leaves problems for user experience.
For sighted users, show indeterminate progress element while form is submitting. For sight-impaired users, use aria live region for accessible status messages (as noted in article).
I don't know of any. I liked that OP pointed it out also the glaring hole that sighted users (or really any user) could still move up to a form element and hit enter to resubmit, thus bypassing that the submit button is disabled.
I agree with some of the perceived issues, but not entirely with the solution.
I think this is what state machines are for. The disabled attribute is in a sense part of the button's own state-oriented behaviours, but as mentioned it isn't really sufficient to prevent other interactions or submissions. You can prevent form submissions and/or ignore them by orchestrating form UI and interactions within a state machine, though.
It sounds complex, but it's an abstraction that can be easily reused across forms, and it's not an expensive abstraction. If you're using JS, it's a trivial addition that can provide easily tested, robust, reliable form behaviours that respect your users and your business logic.
I've wondered many times why something like <form id="my-form
disabled> isn't possible.
> If the button is the current item in focus when you add the disabled attribute (for example, if someone just pressed it to submit the form), the element actually loses focus, which shifts to the document element.
> For a screen reader user, this is particularly jarring, as now you’re in a totally different place on the page.
This just feels like an argument to abandon a very useful, well understood, and built-in tool (disabling to preventing double form submission) in order to make things better for a small subset of users of a particular tool.
Why can't this just be the problem of screen readers? Why is the "solution" to not use or remove a widely used and well understood web standard?
Is there a blogpost out there like "X things programmers don't understand about HTML forms"? In the same vain as the ones for names, character encoding, etc...
Its your job to remove that state if it fails and inform the user. This is just not written down in this post. Thats the part were you have to contact the writer.
“Basic error handling” is distressingly rarely present, and when present, regularly broken.
In general I do think it’s mildly irresponsible to not mention error handling at all and (taking what this article does) to show reenablement only via linear code flow that assumes success. But when it’s in an article about the problems of disabling submit buttons, then it’s even more perplexing.
For me the disabled button on a partially-valid Form say, is a metaphor-overload.
Whilst a physical button may be disabled to indicate being functional-but-not-ready, in software one doesn't have to show/display them until they are ready for action. If the Form is partially valid, show a Message instead of a Button, then when it is valid, pop the Action button clearly on display.
Personally I like to leave the button enabled, check the Action can be performed (form valid etc), and notify the User of errors, progress etc. It's better to communicate with the User exactly, than imply messages - it's a form, not a puzzle or an opportunity to play physical jokes on the User (gotcha, can't press this!).
It would be weird to avoid the only reason I have to use this button attribute. I thought the disabled state only existed to fix this problem. I can't find any other use that wouldn't end up being a complete UX mistake.
In a JS framework frontend I will disable the button on submit, so the form can't be submitted twice, because accidental double clicks happen.
I will enable it after at most 6 seconds or if the response is an error response.
Can we also talk about aggressively deleting form fields? Forgot password? Let me forget the email you just typed in when trying to login. Guessed the wrong password? I'll erase that field for you.
Amazon, of all places, had this defect in their login flow for the longest time which was only recently fixed.
It was so bad that when you were waiting for an SMS code, after you received it and autofilled it in iOS, their website and app would automatically submit the input, and so hitting submit on your keyboard would cause the backend to reprocess the code a second time, invalidating your login.
It was obvious someone slapped the implementation together and it was so embarrassing.
Yet, I’m still nonplussed that we keep screwing up forms.
A long time ago, it became fashionable, even recommended, to disable menu items when they could not be used.
Don’t do this. Users see the disabled menu item that they want to click on, and are left entirely without a clue of what they are supposed to do to get the menu item to work.
Instead, leave the menu item enabled. If there’s some reason you can’t complete the action, the menu item can display a message telling the user why.
We've been following that policy in our desktop application for a long time. It's almost always much better for the users that they get an error message saying why it's not possible to do X when they press the button, than not knowing why the button is disabled.
"almost always" - agreed, but the specific case where your application has clearly gone into a temporary mode where no user interaction is permitted (other than perhaps "cancel") I would think was the exception. But simply adding the disabled attribute to the existing input controls probably isn't the best way to do it if it's an issue with screenreaders etc. Though it's not clear why such tools couldn't deal with it in a better fashion.
I'd also say I'd have no issue with a button showing as disabled provided when you attempt to click it or hover over it, it tells you why it's not yet a permitted action. But again, might be an issue for accessibility etc.
It's much better adding information to the UI explaining why the button is disabled rather than confusing the user to the point he's done an unnecessary action and got an error
Disabling a button is not up for argument. You are entitled an opinion but you are wrong. As mentioned in the article, it becomes invisible for screen readers so, no, hard stop on its usage. From a usability standpoint, an available button gives you the opportunity to validate a users field, shortcut someone to that field and if you use form properly, allow the user to go on to the next invalid field with the enter key.
furthermore, on any app, don't disable a button when an action isn't available. you can still mark it as disabled via color, but if someone clicks on it, you should show them a message WHY that action isn't available. "Sorry, you haven't filled in your phone number", etc.
Allow me to hijack this thread to say something that is related:
Do not disable buttons or checkboxes or whatever unless it's very obvious how to enable them! I have seen software applications that showed disabled form controls or menu entries depending on settings that were in other screens or even compile-time settings - that is unacceptable!
I was going to post this. Many times I've sat there trying to guess what the developer was thinking "maybe this will enable it? no... how about this?..." I can't even imagine what non-techy people do in these situations.
Does anyone remember the "reset" button? I assume it still exists, but to be honest I have never found the need for it. But I do remember it being on many forms that you would encounter pre-2000.
It's clearly difficult if there's so many options and this generates so much discussion. It shouldn't be this hard to get a basic UI pattern right (submitting a form once with progress updates).
If it's even a little hard, it's inevitable 90% of sites will get it wrong. There's just too much stuff for devs to realistically stay on top of to blame them.
Related: the `inert` attribute has good support now for preventing users from editing/interacting with forms that are currently being submitted so you don't have to mess with overriding input events (https://developer.mozilla.org/en-US/docs/Web/HTML/Global_att...). You'll have to add it manually when the form is submitted though, remove it if the submission fails, and use something like aria-live to give progress updates (https://developer.mozilla.org/en-US/docs/Web/Accessibility/A...). You'll also need to manually test all your custom forms like this work on actual screen readers (they have inconsistent behaviour, similar to cross-browser bugs), as well as edge cases like resubmitting after a failure, when the network request fails with error feedback, and when the network request fails from a time out. Not easy.