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.

3 comments:

Benjamin P Lee said...

Assuming under some situations you DO want your form to post (and have set it up accordingly), I wonder if you can bind to the onSubmit event of the form and detect if the submition was caused by an Enter press from your given input box, or some other fashion (Submit button, other input box enter, javascript event invocation, etc.)

Also, I wonder if any browsers support a submit event coming out of an input box. If so, you could bind directly to that.

In either case you would make your code a bit more obscure, but depending on your logic, save yourself the processing time of being invoked on each keypress.

Also, does a TAB in or out result in a key[up|down|press] on the input box.

Interesting post. Gotta love the wonderful world of cross-browser javascript.

Jeff Scudder said...

Great questions Ben. You can indeed set the onSubmit for a form and if it returns false then the browser will not perform the form method-action. Here's a short example:
 
<form method="GET" action="/testtesttest" id="testform">
  <input type="text" id="textinput" name="myText"></input>
  <input type="submit" value="submit" id="submitbutton"></input>
</form>
<script>
  document.getElementById('testform').onsubmit = function() {alert('ok'); return false;}
</script>
However, the default behavior for most browsers is that if you press enter in an text input box in the form, then it "presses" the submit button.

Tabs do register a key down event, but if you want to prevent them from moving focus to the next input, then the event will need to be consumed.

Benjamin P Lee said...

Interesting. In some quick Google-ing after my comment I couldn't determine definitely if an onSubmit event would ever bubble up/down to the actual form element or if you could detect where the onSubmit actually started from.

If you could, you check if the origin was your input and then consume just that one.

Thanks for the reply.