If you've ever written some code that needs to deal with individual keystrokes entered and to 'translate' or parse them, you've probably figured out that while on the surface it all looks pretty easy with DOM event processing, it's actually quite tricky to get accurate key information. There are several keyboard event properties available and - even with jQuery - various browsers handle these event properties slightly differently.

In fact, I realized that almost every time I run into a problem with keyCodes in event handling code I add a bunch of console.log statements to figure out exactly what keyCodes are coming back for each of the available events. So tonight I sat down and threw together a small form that lets me see all this information at a glance on one page.

You can check out my Key Code Tester and play around with it in various browsers:

KeyCodeChecker

Try the Key Code Checker Example

If you try this in various browsers, you'll find that you get a rather large divergence of values for the various events.

But before we look at the actual results and some suggestions, let's back up for a second and explain how key handling in the DOM works.

Key Event Handling for the DOM

The HTML DOM has support for keyboard events that fire when you press keys anywhere in the document. The most common place where the event trapping matters is on input controls like a textbox, but it also can work in any other DOM element like a <div>.

The DOM keyboard input event model has a number of events to handle key events that you can handle:

  1. keydown
  2. keypress
  3. keyup

These events are fairly self-explanatory on the surface and they fire in the order shown above. Each event can be hooked up to an event handler that receives a DOM event parameter which contains information like keyCode, charCode and which as well as shift,ctl, alt states you can look at. Most modern browsers support the keyCode, charCode and which properties. which is a special property that returns the 'significant' value from an event which in the case of the key events tends to be the keyCode or if that is empty the charCode. charCode and which are not available on old versions of IE pre version 9. This can get tricky to check for quickly.and this is where jQuery comes in and provides at least a minimal bit of normalization of these event values.

jQuery Event Normalization of Key Event Properties

jQuery's Event Object supports keyCode, charCode and which properties as well, but you typically should use the which property with it. jQuery normalizes which based on keyCode or charCode. It checks keyCode first and if that's empty reads the charCode which should yield a valid keyboard code.

Back to the keyboard events: keydown fires as a key is pressed down. keypress is fired immediately after keydown, but unlike keydown and keyup it fires only on 'visible' characters that are printable (FireFox and Opera don't follow the spec and always fire). keyup fires after keypress when the keys are released.

At any point during the event handler processing for the keyboard events you can return false which causes the event bubbling to stop. When false is returned further processing stops and key events following the current key event won't fire. If you return false from keyDown, keypress and keyup will not be fired.

Using jQuery it's very easy to handle keyboard events. Here's an example for keydown handling:

$("#txtKey")
    .keydown(function (e) {
    var keyCode = e.keyCode;
    var which = e.which;
    var charCode = e.charCode;
    
    // .. do something for key handling       

    // pass through key stroke with true 
    // keep keystroke from processing further (or get entered) with false 
    return true;
});

Sample Implementation

The above form was implemented using a little bit of script code that basically handles all three key events. It then calls a common function and displays the various key code values in the appropriate box in the page.

The code to do this is pretty simple, but I post it here as it gives a little bit of insight on how the various key event properties work:

    <script type="text/javascript">
        $( function () {
            $("#txtKey")
                .focus()
                .keypress(handleKey)
                .keydown(handleKey)                
                .keyup(handleKey);
        });

        function handleKey(e) {            
            var keyCode = e.keyCode;
            var charCode = e.charCode;
            var which = e.which;

            var type = e.handleObj.origType;            
            var orig = e.orignalEvent;

            if (!keyCode)
                keyCode = "0";
            if (!which)
                which = "0";

            if (type == "keydown") {
                $("#divDown .mainvalue").text(keyCode);
                $("#divCharCodeDown .mainvalue").text(charCode);
                $("#divWhichDown .mainvalue").text(which);

                $("#divCharTyped .mainvalue:eq(1)")
                    .text("")
                    .text(String.fromCharCode(keyCode));

                // also clear out key press values in case it doesn't fire                
                $("#divCharTyped .mainvalue:eq(0)")
                    .html("&nbsp;")
                $("#divPress .mainvalue").text("");
                $("#divCharCodePress .mainvalue").text("");
                $("#divWhichPress .mainvalue").text("");
            }
            else if (type == "keypress") {                
                $("#divPress .mainvalue").text(keyCode);
                $("#divCharCodePress .mainvalue").text(charCode);
                $("#divWhichPress .mainvalue").text(which);
                
                $("#divCharTyped .mainvalue:eq(0)")
                    .text(String.fromCharCode(which));
            }
            else if (type == "keyup") {
                $("#divUp .mainvalue").text(keyCode);
                $("#divCharCodeUp .mainvalue").text(charCode);
                $("#divWhichUp .mainvalue").text(which);

                $("#divCharTyped .mainvalue:eq(2)")
                    .text("")
                    .text(String.fromCharCode(keyCode));                
            }

            

            // must return true in order for keypress to fire
            // in all browsers. Remove character using setTimeout 
            // to delay and clear text after the fact.
            setTimeout(function () { $("#txtKey").val(""); }, 2);

            // always return true so keypress fires
            return true;
        }        
    </script>

Browser Divergence

I mentioned earlier that various browsers handle the various key codes differently. Check out the following when pressing the 'a' key in FireFox, Chrome, IE (8 standards mode), Opera respectively:

browserDifferences

You can see the divergence here. FireFox doesn't get a good keyCode in the keyPress event, and instead gives a charCode. All other browsers tested return a valid keycode in all events. Notice that all browsers return consistent results with jQuery using the e.which property. The which property is clearly what you should use to get a reliable and consistent keyCode value that works across all browsers.

Suggestions

  • Use e.which when checking for Key Codes
    When using jQuery it's best to use e.which in key events to check for key codes. e.which normalizes between all key combinations that I saw and provides the most consistent value across browsers.
  • If you need a Character Value from a Key Code use keypress Event
    When you need to get a character value from a key event handle the keypress event as it's the only one that can receive a translated key code that reflects the actual character that the user typed and that would appear in the text box. You can use String.fromCharCode(e.which) to get the printable character.
  • Watch out for keypress Event Differences
    Keep in mind that the keypress only fires on printable characters and that the key code value for e.which will be different than those in keydown and keyup. If you need to handle both printable and special keys you might have to implement both keyDown and keyPress.

Hopefully this utility plus some of these suggestions will prove useful to some of you. I know it will be to me the next time I have a need to manipulate keystrokes in my JavaScript code - information like this has a half life of about 2 days with me after it files out of my brain again :-). This blog post should help me remember next timeā€¦