Detecting (And Canceling) Key-Combo Events With jQuery
The other day, I was trying to prevent a user from entering certain restricted characters in an input field. The approach was simple: detect which key was being pressed; and, if the key mapped to a restricted character, cancel the default event behavior, thereby preventing the input from being altered. Only, it wasn't working. After a few minutes of debugging, I finally realized that the keydown and keypress events worked differently than I had previously thought.
In jQuery, keyboard-based events build a "which" property into the event object. I had always thought of this property as being the ASCII value of the character being rendered. But, as it turns out, it's a bit more complicated than that.
From what I've read as part of my debugging effort, it seems that the "which" property of the keyboard event represents different things in different event handlers. In the keydown event, the "which" property represents only the key being pressed - not the character being rendered. In the keypress event, on the other hand, the "which" property represents the ASCII value of the character rendered by the keyboard event.
To demonstrate this, I put together some code that logs key codes using both the keydown and keypress events:
<!doctype>
<html>
<head>
<title>Detecting Key-Combo Events With jQuery</title>
<script type="text/javascript" src="./jquery-1.7.1.js"></script>
<script type="text/javascript">
// Run when the DOM has loaded.
$(function(){
// Let's define a generic event handler that will look at
// the key being pressed and log it out to the console.
var keyHandler = function( event ){
var keyCode = event.which;
var keyChar = String.fromCharCode( keyCode );
// Log the key captured in the event data.
console.log(
event.type + " : " + keyChar + " (" + keyCode + ")"
);
};
// Now, let's try binding both the key-down and key-press
// events to listen for the key and combos.
$( "input" ).on( "keydown keypress", keyHandler );
});
</script>
</head>
<body>
<h1>
Detecting Key-Combo Events With jQuery
</h1>
<form>
<input type="text" value="" size="30" />
</form>
</body>
</html>
With both event types bound, if I focus the input field and type the "?" character (using SHIFT + ?), I get the following messages logged to the console:
keydown : (16)
keydown : (0)
keypress : ? (63)
As you can see, the SHIFT key registered as key code 16 during the keydown event. When I then pressed the "?" key in combination with the SHIFT key, the ASCII value of the question mark character - 63 - was not available until the keypress event.
I've always thought of the keydown and keypress events as being somewhat interchangeable. And, for most situations, they can be. But, when it comes to figuring out which character is going to be rendered - especially those requiring key combinations - you have to listen to the keypress event.
Want to use code from this post? Check out the license.
Reader Comments
Interesting find, I'll have to keep that in mind when capturing key events. On another note, whatever are you using? I like the color coding.
Thanks
- Steven
@Steven,
I recently started using Sublime Text 2. When I was at the recent cf.Objective() 2012 conference, everyone was talking about it for being super fast and just a pleasure to use. So, I thought I'd give it a try. I'm still in "trial" mode; but, I think I'll probably guy it.
I still have to learn the keyboard shortcuts and create the snippets I'm used to having. But, from everything that I can tell, it supports pretty much all of the functionality I'm used to having.
@Ben,
Thank you, I'm gonna give it a shot.
If your intent is to "prevent a user from entering certain restricted characters in an input field", you'll also need to detect mouse paste events.
Some developers prevent pasting in an attempt to prevent characters, but that's not a good way of doing it.
Check out ZURB's "jQuery Text Change Event". It adds textchange, notext and hastext events that can be bound to input & textarea fields:
http://www.zurb.com/playground/jquery-text-change-custom-event
A resource I use when working with key filtering.
http://www.w3.org/2002/09/tests/keys.html
@James,
Excellent point. I think for the actual solution that I ended up using in my production code, I actually used a keypress handler to both:
1. Prevent the given character.
2. Do a RegEx replace across the entire string.
While #2 technically takes care of all solutions, the reason I included both #1 and #2 was to prevent latency in the replace. Preventing the default event provides a faster experience than a replace() method call (which momentarily shows the invalid characters in the input).
@Grumpy,
Cool widget! Thanks for the link.
@Ben, Were you trying to prevent an ALT+#### input?
This may be covered by what James mentioned, and may not be necessary depending on the audience for your form, but non-keyboard entry, like the Character Map (Win) or Character Viewer (Mac) and the On-Screen Keyboard (Win) or Keyboard Viewer (Mac), may not necessarily generate key events. International characters could be an issue as well, or a non-issue if your validation isn't that specific (or if that part of your audience is small enough).
@Randall,
We were trying to prevent the input of some characters that are restricted in some file systems (like * ? / \ | : ... etc). While these are meaningless online, we are working on some file-integration stuff which requires us to add some restrictions.
@Dave,
Very good point! We are also doing validation on the server-side, so nothing should slip through the cracks. The client-side stuff was more for a seamless user experience than it was for error catching. But, good thinking - I completely forgot how many ways there are to enter stuff into an input ... not to mention using FireBug to edit the underlying HTML!
I was updating one of the jQuery plugins that I use to filter input fields to allow only numeric characters and I realized that it did what you wanted & it also uses ReGex.
http://treyhunner.com/2010/10/replacement-for-jquery-alphanumeric-plugin/
The original plugin had to be rewritten due to a number of browser-specific features that were allowing entry of illegal characters.
@James,
Very cool, I'll take a look!
I have been dealing with key events for sometime with my plugin autoNumeric and have been largely successful with desk-top browsers.
Android browsers is a whole different beast with keydown events throwing the same e.which codes and no keypress events.It varies from browser to browser.
Using ReGex will be critical until these browsers become mature and have familiarity to their desktop counterparts.