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.
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.
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?
It is and there is a ready-made solution to handle it https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ET... Your HTTP library likely already has support for it.
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.
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.
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.
[1]: https://www.ietf.org/archive/id/draft-ietf-httpapi-idempoten....
[2]: http://live.julik.nl/2021/12/actually-creating-a-gem-for-ide...
No, users can still double-click the submit button to create multiple requests. I don't see how you can solve it without JS.
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.
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)
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.
https://drupal.stackexchange.com/questions/69803/cache-form-...
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.
> , 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
What original fundamental feedback? The default disabled style of a form button is not what a button should change to after submit?
Replacing a submit button with a dummy is wrong, it does not solve the accessibility issue where focus is lost or moves.
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.
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.
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.
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.
Also there's standardization and coordination between browsers, but if there is a genuine need for something they tend to find a way.
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.
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.
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.
Do we really now have to work around broken screen readers too? Fuck that.
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.
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.
It's a shame that screen-reader support sucks, but it's semantically correct.
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.
----
Mozilla writes about this exact scenario (https://developer.mozilla.org/en-US/docs/Web/Accessibility/A...):
> 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.