RSS
 

Posts Tagged ‘html 5’

Cross browser oninput event for jQuery

18 Oct

Update: after a long period of inactivity, I didn’t realise there was a minor bug in the code. Couple that with the fact that it didn’t work in newer versions of jQuery, and I decided to remove the code from this page. However, there’s a fork of the code available at https://github.com/dodo/jquery-inputevent, which works with newer jQuery versions.

After 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 that input is a bubbling event and propertychange is not, or that detecting support for input is made extra difficult by Firefox. Instead of expanding that article, I decided I would write a snippet to try and normalize the event a little better. I chose jQuery as a base for the code, mostly because I wanted to learn more about writing custom events for jQuery, but partly because I knew it would make a lot of things easier. The result is the input event plug-in below.

I wasn’t really sure where to start, but Googling turned up Ben Alman’s excellent jQuery special events post which tells you more or less everything you need to know. After that, I was able to create a basic plug-in that would provide an interface for jQuery.fn.bind("input"). The plug-in decides between attaching to input, propertychange or a host of fall back events that may or may not be supported in the browser. In short, it tries to get as much of the functionality of input as possible, but there’ll always be at least some functionality, for instance, plain keyboard input detection.

Note that the plug-in does have the following limitations in browsers that don’t support input:

  • There’s no “catch-all-changes” fallback. I had considered running a timer, when an element is focused or moused over, that constantly checks for value changes. However, this would trigger when the value of the element is changed programmatically, which may not be desired.
  • Because IE’s propertychange triggers when the value of an element is changed programmatically, that browser suffers from the problem described by the limitation above this one. It is possible that an infinite loop could be caused by always changing the value of the element in the event handler. A workaround, if you need to do this, might be to temporarily unbind whilst you change the value, rebinding after changing.
  • It currently only works with input and textarea elements. I may be able to get some support in for contentEditable elements in the future, but they don’t fire a propertychange event in IE when typing or pasting, which makes them awkward for capturing input. Firefox, Opera and IE9 don’t support the event on contentEditable elements anyway.
  • It will not work with live() or delegate(). This is due to how the propertychange event doesn’t bubble. Event delegation works as of version 1.1
  • It doesn’t work around the bugs Opera has with the input event. I’m not sure which versions are affected and it would be a pain to figure it all out. The plugin should now provide the best level of compatibility possible in all versions of Opera.

Changelog

  • v1.1: Oct 24, 2011 — Fixed many browser implementation issues, introduced live/delegate compatibility, changed event special name to txtinput. see full post.
  • v1.0: Oct 18, 2010 — 1st version

The following fiddle (very basically) demonstrates the event:

[iframe http://jsfiddle.net/AndyE/sdkBs/embedded/result 99% 300px]

Thanks to: Nick Craver, Tim Down, Ben Alman and Daniel Friesen. Please post any issues you come across in the comments.

 

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 http://jsfiddle.net/jKrQK/show/light 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 http://jsfiddle.net/Gn7Rm/show/light 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 http://jsfiddle.net/hQxxY/show/light 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.