At the very least, you should consider which is appropriate for which situation, what if, in your UI, for some buttons one is the obvious choice, for others it's the other, but for some it's not so clear, and both behaviours are defensible? Now you've got an inconsistent UI.
I have no good solution for this.
The buttons in an elevator panel typically work this way. They each light up to confirm a pending request to reach a floor. They each turn off when its floor has been reached. And while a button is lit up, pressing it does nothing.
It would help if GUI elements had a property “automatically disable on click”, removing the need for the “on click handler” to disable the button (in exchange for adding the need to explicitly re-enable it).
I don’t remember seeing GUI libraries that do that, though.
That probably is because it would confuse users if buttons visually get disabled when they click them.
So, the best answer is to visually keep the button enabled, but ignore rapid further clicks. That’s debouncing.
The visual representation updating (greying out button) is a result of disabling the button, not the same thing. In virtually every GUI toolkit I've ever used there is the concept of the main UI thread, and everything that happens (input and display updates) necessarily has to go through that single thread in order to ensure correctness. (This applies to browsers, too.) That's why input goes into a queue, so you can easily do things like:
(All on the main UI thread):
- Receive click event 1: disable button, start background process. Possibly redraw button UI *but it doesn't matter because the UI display is not the state, it's just a view*.
- Receive click event 2: nothing happens, button is disabled
- Background process finishes, posts update to re-enable the button
- Receive click event 3: disable button, start background process, etc.My favorite example of doing it wrong is a log in form: if the login button is clicked twice, the server would reject the login because the first click has already used up the one-time token so the user gets an error page.
But I think the biggest problem is that people either apply denouncing to all buttons in a UI (like turning it on within the framework they are using) or apply denouncing to nothing. So there really isn’t a culture for carefully considering which situations warrant which.
For users with JS disabled, your solution seems good.
If the operation is idempotent, well, clicking twice doesn't do anything. I'd still want to see the button light up to signal that the UI is alive, or the button could grey out or enter a "latched" state like a radio button if there is nothing to be done. Behind the scenes suppressing command propagation is an implementation detail and the trade off is between front-end complexity and redundant command execution overhead.
If the operation is not idempotent I can give you separate examples where different behaviors are appropriate:
1. A button used to increment a counter (e.g. quantity of GPUs to buy) should increment on every click, even if the UI response is delayed. The user can count clicks, and there is going to be a decrement button to reverse any error. You do not want the user waiting around guessing whether the software is still processing the remaining clicks. As a rule, so long as the operation is non-destructive (e.g. inc and dec buttons, all operations reversible/undoable, etc.) every user interaction can and should be actioned.
2. A button used to perform an irreversible action, i.e. a "commit", such as placing the order to purchase a GPU, should only perform that operation once. I would not call this an idempotent operation, certainly not with respect to your bank balance.
> The Nothing Phone button gives you a tap confirmation via both haptics and sound, and then ignores the tap if a previous rotation is still animating.
This is the issue. Number of performed actions has to be equal to number of times the app identified that button press was registered. Debouncing is a good practice, but if it is used then debounced taps must not produce feedback.
I was really confused at their mention of accessibility, because my mind jumped to people with hand tremors who would double press when they intended only one press.
And then, of course, there are the people that double-click every button. To handle that, disabling a submit button in the onclick is very common.
I couldn't even finish the last Apple presentations as it all feels so stiff, inhuman and run by suits, they all seem like robots scared of diverging from the holy script who will get fired if they display emotions and humanity.
Off-topic perhaps, but got reminded how delightful even the somewhat messy ad-hoc presentations from Jobs were.
Absolutely everything seems scripted including hand movements, shifting of postures, smiles..the whole works.
Now I just wait for the press release and that’s that.
Which to me makes no sense, surely you have some budget for risks if you are a 1T company? I suppose their risks are more moonshots to some degree like the Apple Car but nevertheless, I do miss the old presentations.
1. "The Nothing Phone button gives you a tap confirmation via both haptics and sound, and then ignores the tap […]"
2. There is a really good reason to tap this button 3 times in a row.
Nowadays, you use transitions instead, which are not queued. But I still very occasionally see things that use queued animations.
On physical keyboards we already have three different kinds: normal buttons, modifier keys (shift, etc) and toggle keys (caps lock).
High stakes rare actions can require special button designs. E.g. on a black magic cinema camera the button that formats the memory card needs to be held for three second while it visually counts down. This gives a small delay during which the user can decide: "Fuck this is the wrong memory card!" and cancle.
The downside is that some imaginary power user that uses the camera only to format a stack of SSDs will get burdened. You have to decide which is more common and make a decision.