Effectively detecting user input in JavaScript

22 Sep

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 100% 60px]
View source

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 100% 60px]
View source

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 100% 60px]
View source

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.


Tags: , , , , , ,

Leave a Reply


  1. Cross browser input event for jQuery « What the Head Said

    October 18th, 2010 at 4:36 pm

    [...] posting my first technical blog post, Effectively detecting user input in JavaScript, I felt like I needed to expand on a few things that I didn’t cover. For instance, the fact [...]

  2. Using the oninput event with onkeydown as its fallback

    June 5th, 2011 at 4:40 am

    Using the oninput event with onkeydown as its fallback…

    HTML5 standardizes the oninput event handler, which should be used to detect user input in JavaScript. Sure, you could use onkeydown or onkeyup instead, but those were never really designed for this particular use case, and it shows. […]…

  3. anita

    August 2nd, 2011 at 6:25 pm

    Thank you! everything works fine except I still have a problem when I press the back button on the text box.

  4. David

    September 17th, 2011 at 1:16 pm

    Great post. I didn’t know about oninput. And I was getting confused with what do use: onkeyup, onkeydown, or onkeypress lol

  5. [分享]哥俩好:oninput & onpropertychange « NeverBest!我还能做的更好 – 007boy | im007boy

    October 18th, 2011 at 3:10 pm

    [...] 《Effectively detecting user input in JavaScript》 [...]

  6. HTML5 Input Event Handlers and User-Experience « GirlieMac! Blog

    November 27th, 2011 at 8:00 am

    [...] The answer is input. I did not know about the newly introduced HTML5 oninput event handler until I found this article. [...]

  7. Anonymous

    May 28th, 2012 at 5:08 pm

    [...] 《Effectively detecting user input in JavaScript》 [...]

  8. Marek

    August 21st, 2012 at 8:43 am

    Great post. Saved me a lot of time looking for the right approach on stackoverflow :)

  9. Oldřich Válek

    December 15th, 2012 at 4:56 pm

    I think it works the best. (keyup, click, change, paste with mouse context menu…)

  10. Joy

    May 22nd, 2013 at 9:10 pm

    Thanks for the explanation!
    Your iframe shortcodes aren’t working on this page…