Mouse vs. Keyboard - Determining Click Initiator Using A jQuery Custom Event
When filling out online forms, I love to use my keyword as a means to both provide information as well as to navigate from form field to form field. This works great; but from time to time, an "itchy Tab finger" causes me to accidentally hit "Enter" on an inappropriate form element (such as a Cancel link). Falling victim to this problem the other day, I wondered if there was a way to determine which device - the mouse or the keyboard - triggered the "click" event. If I could, then I thought it might provide an opportunity to confirm an action if, say, a Cancel link were triggered from the keyboard rather than the mouse.
There are two ways for a user to activate a link: either clicking the link directly with the mouse; or, bringing the link into focus and then hitting the Enter key on the keyboard. Both of these actions trigger a "click" event. But, after I looked at the Event object being produced, I couldn't find a consistent, cross-browser way to determine which device initiated the event.
As such, I thought I might try to create a custom jQuery Event Type - "clickwith" - that would piggyback the native click event and include keyboard conditions during the event dispatch.
Before I get into the code, I should warn you that the jQuery custom Event system is a bit of mystery to me. I can get it to, "work." But, I don't necessarily have a great mental model of how it all fits together or how the underlying events are bound and dispatched. And, forget about including data and namespaces in the mix - that's already way beyond my current understanding.
That said, the idea behind this custom event, clickwith, is that we'll bind some key events as well as a click event. The key events - keyup and keydown - will keep track of key activity surrounding the click event. Then, when the custom "clickwith" event is being dispatched, it can use this key-based metadata as part of the outgoing event data.
The pseudo-code for the approach is as follows:
- If key pressed and key is Enter, set flag to True.
- If click event is raised and flag is true, announce keyboard.
- If click event is raised and flag is false, announce mouse.
- If key released, set flag to false.
As you can see, the keyboard events simply set a flag that is used within the click event handler. Ok, let's take a look at the code:
Again, the way the special Events work in jQuery is not something that I have a strong grasp of. As such, I won't try to explain this code too much. If you look at the video above, however, you will see that this is working both for locally-bound events as well as delegated event bindings.
For a very in-depth exploration of jQuery special events, take a look at this article by Ben Alman - his understanding is a world better than mine.
Want to use code from this post? Check out the license.
"...I love to use my keyword as a means to both provide information as well as to navigate from form field to form field."
Ha ha, nice catch! Best case scenario, I just fill fields in with my Mind :D
what if u monkey patch the jQuery click event to return a property like let's say origin: "keyboard" or origin: "mouse"?
I am curious if the new movement toward touch screens might negate this question altogether? Windows 8 is designed to move us to that environment
Thanks Ben, that's very useful. I was going round the bend trying to figure out the source of a click event. @ross: touchscreens might make this more relevant, not less. In fact, I think there's a case for building this into jquery core, so that you could easily query an origin property on any click event : mouse, keyboard, or touch.
On second thought, if you look at the top level of the jquery Event object, it is pretty weird that keyboard events are in fact stored as mouse events. But if you dig a little deeper into the object, you can see that X-Y coordinates are stored for each click. For example, "clientX". For clicks that come from the enter key, these coordinates are always zero. (Nobody ever mouse-clicks at 0,0.) This essentially creates a simple hook that you can use within click event handlers to discriminate keyboard "clicks" from mouse "clicks".
Thanks dude! This fixed the problem I was having to differentiate between the keyboard or mouse that triggered the click! :)