Thursday, October 22, 2009

The joys of onKeyDown

I recently found myself in need of a way to detect when a user presses enter when typing in a text input box and happened upon one of the quirkiest of browser features, key press detection in JavaScript. Searching the Internet provided a few ideas but nothing that quite fit what I was looking for, so I thought maybe I should get the word out about what I've learned.

Lets assume that we have a form with an input box and we want to perform some special action when the user presses the enter key while focus is on the text input box. We begin by adding an onKeyDown event to the input box (you might also consider onKeyUp, but onKeyPress seems like it might be somewhat quirkier).
<form>
<input type="text" id="my-input">
</form>
<div id="output"></div>
<script>
document.getElementById('my-input').onkeydown = function(e) {};
<script>
Now the first hurdle is that in IE, Safari, and Chrome you can access the ASCII character code for the key which was pressed in window.event.keyCode but in FireFox you'll need to get the event object and look at the which member.
<script>
document.getElementById('my-input').onkeydown = function(e) {
var keyCode = 0;
if (window && window.event && window.event.keyCode) {
keyCode = window.event.keyCode;
} else if (e && e.which) {
keyCode = e.which;
} else {
alert('fail');
}
document.getElementById('output').innerHTML = 'You pressed ' + keyCode;
};
<script>
Now there is one gotcha with what we've written so far. Each key that we press when focus is on the desired input causes our code to be run, but the browser also performs the default behavior for that key. If our input is a simple text box, the characters pressed will show up in the text box. If the input is part of a form with a submit button, pressing enter will cause the form to be submitted. If the form is submitted then the browser is sent to a different page which in this case is not what we want.

There are a few ways to prevent the key presses from also triggering the default behavior and do only our behavior. One of the simplest ways is to return false from the onKeyDown function. If we change our handler to the below, characters will not show up in a text input box because our handler consumes them.
 document.getElementById('my-input').onkeydown = function(e) {
var keyCode = 0;
if (window && window.event && window.event.keyCode) {
keyCode = window.event.keyCode;
} else if (e && e.which) {
keyCode = e.which;
} else {
alert('fail');
}
document.getElementById('output').innerHTML = 'You pressed ' + keyCode;
return false;
};
With the above, the last key press is displayed in the output, but in most cases we probably do want the user to see the chracters they've typed showing up in the input box. We just want to prevent the form submit when the user presses the enter key. To accomplish this behavior, we can return false when we don't want the key press to propoage and return true when the default behavior should also be performed.
 document.getElementById('my-input').onkeydown = function(e) {
var keyCode = 0;
if (window && window.event && window.event.keyCode) {
keyCode = window.event.keyCode;
} else if (e && e.which) {
keyCode = e.which;
} else {
alert('fail');
}
document.getElementById('output').innerHTML = 'You pressed ' + keyCode;
if (keyCode == 13) { // 13 is the key code for the enter key.
return false;
} else {
return true;
}
};
There you have it, a way to detect when the enter key is pressed in our input box while also preventing the form from being submitted.