Like I said in my introductory post, my inspiration to start blogging came from Stack Overflow. All too often, I see the same questions answered in the same way by different people and I think to myself, “No, it shouldn’t be done like that”. Here’s one typical question:
“Hi, I’m trying to get the value of an input box as the user types into it.”
Almost instantly, you see the wrong answer appearing several times – “Use the onkeyup event handler. It’s great and does all things!”. The sad thing is that this is often the accepted answer. “What’s wrong with this”, you may ask? Well for one, the onkeyup event only fires when you lift your key from the keyboard. The worst part is that these are often marked as the correct answer by the person asking the question. This might not be a huge problem for you, but you might also be unaware that there can be a varying delay between when the value of the text input changes and when the user lifts their finger from the key. Take the following example, type and might notice a slight delay before the text input’s value is copied to the div below it:
[iframe http://jsfiddle.net/jKrQK/show/light 100% 60px]
Now hold your finger down on a key and let your OS’s input repeat trigger. Notice how the div doesn’t update even though extra characters are added to the input? This doesn’t feel very professional, does it? Often, I provide a solution to these questions by means of a timer in the onkeydown event handler. This still only handles input from a keyboard, but it feels less clunky as the update is almost instant.
[iframe http://jsfiddle.net/Gn7Rm/show/light 100% 60px]
The timer is set with a 0ms value so that it is queued to be executed almost immediately. I say almost, because all browsers have a minimum delay on setTimeout and setInterval (Firefox’s is 4ms), but it’s still a better solution than waiting for the onkeyup event and a much faster response for the user. However, this still doesn’t deal with input that doesn’t involve a keyboard. What other types of input? How about context menu actions; cut, paste, undo and redo? Drag and drop? There have been events to handle some other kinds of input change for a while – several browsers support the onpaste, oncut and oncopy events – but they’re non-standard and some vendors are just too stubborn to implement them.
Enter the HTML 5 event, oninput. This event really makes sense – one handler to rule them all, and browser support is looking great so far. There’s just one exception to the list… wait for it… Internet Explorer! So we’re back to square one. Or are we? Well, the oninput event can be emulated in Internet Explorer by handling the onpropertychange event. This event will fire when any property changes on a DOM element, so all that’s needed is a simple check to make sure the property is element.value. A proper implementation would check the input’s type property too, since on checkboxes and radio inputs the oninput event should fire when the state changes. So let’s check out our latest solution, go nuts with copying, pasting, dragging and whatever!
[iframe http://jsfiddle.net/hQxxY/show/light 100% 60px]
And there you have it. If you need to support older browsers (note that this works in IE6+), you can use your favourite method of event detection to fall back to onkeydown with a timer. At least then you’ll have at least some of the desired functionality. The added benefit here is that this solution provides us with a method of validating user input when it is pasted or dropped into the box as well as typed, so you don’t have to wait until the change event to check for this. I might touch on that in a later post, I think I’ve written enough for one day.
Note: There’s a bug in Opera 10.x where oninput doesn’t fire on certain elements for certain actions (like cut, copy, paste, drag & drop). It does work fine for textarea elements, however, and this event still works better than the onkeyup even with this setback. I’ve reported this issue to the Opera developers and hopefully they’ll fix it soon (edit: it’s fixed in Opera 11 alpha). See also the bug affecting Firefox’s implementation of oninput.