Detecting (And Canceling) Key-Combo Events With jQuery

Posted June 22, 2012 at 10:23 AM by Ben Nadel

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.




Reader Comments

Jun 22, 2012 at 10:54 AM // reply »
4 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


Jun 22, 2012 at 10:57 AM // reply »
11,238 Comments

@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.


Jun 22, 2012 at 11:20 AM // reply »
4 Comments

@Ben,

Thank you, I'm gonna give it a shot.


Jun 22, 2012 at 11:25 AM // reply »
46 Comments

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


Jun 24, 2012 at 3:57 AM // reply »
18 Comments

A resource I use when working with key filtering.

http://www.w3.org/2002/09/tests/keys.html


Jul 2, 2012 at 9:35 AM // reply »
11,238 Comments

@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.


Jul 2, 2012 at 11:13 AM // reply »
158 Comments

@Ben, Were you trying to prevent an ALT+#### input?


Jul 2, 2012 at 2:58 PM // reply »
35 Comments

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).


Jul 5, 2012 at 9:51 PM // reply »
11,238 Comments

@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!


Jul 17, 2012 at 11:20 AM // reply »
46 Comments

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.


Aug 18, 2012 at 10:32 AM // reply »
11,238 Comments

@James,

Very cool, I'll take a look!


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 22, 2013 at 11:47 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dana, Awesome - so it looks like this bug was fixed in ColdFusion 10. Thanks so much for double-checking that. ... read »
May 22, 2013 at 11:37 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
When I c&p and run on cf10, I get: Selected User IDs: 1,4 User 1 selected: YES - YES User 2 selected: NO - NO User 3 selected: NO - NO User 4 selected: YES - YES User 5 selected: NO - ... read »
May 22, 2013 at 11:27 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Tom, Good thought, but no dice. Both of these still exhibit the same behavior: users.id[ users.currentRow ] users[ "id" ][ users.currentRow ] It's just something whacky happening with ... read »
May 22, 2013 at 11:07 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
Could your problem be that "users.id" is actually an ARRAY, not a single value? Perhaps try it again with "users.id[1]" (I only have CF8 here at work). ... read »
May 22, 2013 at 7:52 AM
Nested Views, Routing, And Deep Linking With AngularJS
Hi, Just a quick thank you. As it happens, for my own purposes, the pending ui-router work being done in native angular is likely the one I'll adopt, but your exploration, code and documentation of ... read »
May 22, 2013 at 4:43 AM
How Do You Use The ColdFusion CFParam Tag?
'<cfparam>' or 'isDefined()and <cfset>' performs the same task.Is there any difference? ... read »
May 21, 2013 at 7:46 PM
Using Plupload For Drag & Drop File Uploads In ColdFusion
No luck. At least I have uncovered the cause, URLScan 3.1. Here is what I see in the IIS log when a file is over 30mb. 2013-05-21 23:29:05 10.105.45.128 GET /plupload/assets/jquery/jquery-1.8. ... read »
May 21, 2013 at 6:12 PM
Using Plupload For Drag & Drop File Uploads In ColdFusion
Ben, I did not see you after Pete Freitag's Lockdown session at cfObjective but he said that IIS sets file size limits at 30MB by default which just happened to be the threshold for file size when ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools