←back to thread

293 points ulrischa | 2 comments | | HN request time: 0.415s | source
Show context
nozzlegear ◴[] No.42174177[source]
For anyone who didn't click through to the WebKit bug report the author submitted, a WebKit dev asked him to clarify why the BBC finds it beneficial to be able to detect that the event was sent from a keyboard. This is the author's response:

> Ironically, I want interoperability on this to help with use cases relating to accessibility.

> I work at the BBC and, on our UK website, our navigation bar menu button behaves slightly differently depending on if it is opened with a pointer or keyboard. The click event will always open the menu, but:

> - when opening with a pointer, the focus moves to the menu container.

> - when opening with a keyboard, there is no animation to open the menu and the focus moves to the first link in the menu.

> Often when opening a menu, we don't want a slightly different behaviour around focus and animations depending on if the user 'clicks' with a pointer or keyboard.

> The 'click' event is great when creating user experiences for keyboard users because it is device independent. On keyboards, it is only invoked by Space or Enter key presses. If we were to use the keydown event, we would have to check whether only the the Space or Enter keys were pressed.

Source: https://bugs.webkit.org/show_bug.cgi?id=281430

replies(5): >>42174432 #>>42174435 #>>42174511 #>>42174692 #>>42175176 #
amluto ◴[] No.42175176[source]
This is fascinating, because the naive English interpretation of the code and the comment on that WebKit bug don't match the actual structure of the code. Here's the relevant code:

    const isInvokedByMouse = event => event.screenX > 0 || event.screenY > 0;
    const isInvokedByKeyboard = event => isEnterKey(event) || isSpaceKey(event);
Ignoring the actual conditions entirely, this code seems to be trying to categorize the event into one of two categories: mouse or keyboard. But what it actually does is to categorize into one of four categories: (mouse and not keyboard), (keyboard and not mouse), (keyboard and mouse), and (neither keyboard nor mouse). And, as the original bug shows, (neither keyboard nor mouse) is handled inappropriately. One might wonder whether (keyboard and mouse) works well.

Either the code should be deliberate about the fact that (is it a keyboard) and (is it a mouse) are separate booleans, or the code should be structured so that the actual categories are mutually exclusive. For example:

    const isInvokedByMouse = ...
and use !isInvokedByMouse to check for keyboardiness, or:

    const eventSource = ... (returns "keyboard" or "mouse")
or, perhaps even better:

    const eventSource = ... (returns "keyboard", "mouse", or "not sure")
replies(4): >>42176623 #>>42177520 #>>42179647 #>>42186662 #
1. taneq ◴[] No.42179647[source]
Well said. I usually go through several phases when dealing with this kind of thing, I start with "flags for different conditions" type logic, then when things get too complex I refine this into "set of explicitly defined states", and then as the state code evolves from 'ideal happy path' to 'production-tested code' I gradually realise that most of the original combinations represented by the original flags actually CAN happen in weird edge-cases.
replies(1): >>42180092 #
2. chii ◴[] No.42180092[source]
> "set of explicitly defined states"

this is called algebraic data type (https://en.wikipedia.org/wiki/Algebraic_data_type), and it is the best way, imho, to reduce bugs in code.

By making it easy to pattern match, it reduces the possiblity of producing an invalid state, because at the time of definition, you have to figure out how to get that type (and checked by compiler).