Mathias Bynens

Using the oninput event handler with onkeyup/onkeydown as its fallback

Published · tagged with DOM, HTML, JavaScript

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.

Luckily, all modern browsers support oninput, IE9 included. For older browsers it’s a good idea to fall back to the keydown event, for example. Unfortunately, detecting oninput support isn’t as straight-forward as you’d think. I assumed this JavaScript snippet would return true or false, depending on whether oninput is supported or not:

'oninput' in document.createElement('input');

This works correctly in most browsers, but not in Firefox (see bug #414853 (now fixed)). While it’s still possible to write a working feature test for oninput, it’s really cumbersome.

Besides, there’s no need to feature test — just bind handlers to both the input and keydown events, and then remove the onkeydown handler as soon as the oninput handler fires. Here’s a simple example, DOM0-style:

someElement.oninput = function() {
this.onkeydown = null;
// Your code goes here
};
someElement.onkeydown = function() {
// Your code goes here
};

The keydown event will only fire once (since it fires before oninput) – after that, only oninput will be used. That’s not ideal, but it sure beats adding lines and lines of code just to properly detect oninput support in all browsers.

A simple demo is available. Note that the same pattern can be applied for any event handler that has a lesser alternative in older browsers.

About me

Hi there! I’m Mathias. I work on Chrome DevTools and the V8 JavaScript engine at Google. HTML, CSS, JavaScript, Unicode, performance, and security get me excited. Follow me on Twitter, Mastodon, and GitHub.

Comments

wrote on :

Of course, since you’re probably re-using the same code in both handlers, you could use a separate function:

function update() {
// Your code goes here, e.g.
console.log(this.value);
}
someElement.oninput = function() {
this.onkeydown = null;
update.call(this);
};
someElement.onkeydown = function() {
update.call(this);
};

If you’re using jQuery events, you could use jQuery#unbind:

function update() {
// Your code goes here, e.g.
console.log(this.value);
}
var $someElement = $(someElement);
$someElement.bind({
'input': function() {
$someElement.unbind('keydown');
update.call(this);
},
'keydown': update
});

Here’s a quick jQuery plugin (GitHub repository):

$.fn.input = function(fn) {
var $this = this;
if (!fn) {
return $this.trigger('keydown.input');
}
return $this.bind({
'input.input': function(event) {
$this.unbind('keydown.input');
fn.call(this, event);
},
'keydown.input': function(event) {
fn.call(this, event);
}
});
};

Use it as follows:

$('.any-number-of-elements').input(function(event) {
alert(this.value);
event.preventDefault();
});

Note that namespaced events are used to prevent unbinding other jQuery-bound keydown event handlers. Also, in addition to keydown, you could bind to the cut, paste and drop events as well.

Zoltan Hawryluk wrote on :

Great idea doing the double oninput/onkeydown trick! Coincidentally, I was also doing some research on oninput as well. IE9 has a really annoying bug where backspace and delete (as well as cutting parts out of the input element) were not firing the oninput event. :-( I hope you forgive the shameless self-promotion, but I wrote an article about how to fix this problem, and how I updated my HTML5 forms polyfill, html5Widgets to fix these issues as well as add support in older IEs.

Note that this only fixes it in the <form> element, which I find the most useful since it will fire the oninput event when any value in any input of the form is modified. I am going to do more research before I fix it for all the others, since I want to research other issues before I support more tags (the Firefox bug you state above may be one of them).

Andy Earnshaw wrote on :

I’ve found that onpropertychange will give results similar to oninput, so when not using jQuery (which I have a plugin for), I use the following code:

if (!("oninput" in document.body)) {
element.onpropertychange = function() {
if (event.propertyName == "value")
this.oninput && this.oninput(event);
}
}
element.oninput = function() {
// …
}

Since Firefox and other browsers have supported oninput for a while, I decided that detection isn’t really necessary. I would say that I get more IE 6 users on my sites than Firefox 1 or Opera 8 :-) For the ultra paranoid, it would be easy to combine your code with mine and have IE 5.5+ working just as good as Chrome 11, with the fallback in place just in case a visitor happens to be using Opera 8.

Tim Down wrote on :

One annoyance is that Safari prior to version 5 doesn’t support the input event for textareas. It does however support a textinput event instead.

Irfan wrote on :

Hello Mathias,

Considering that mouse related events should — whether they do or not, I do not know — essentially constitute input, as well, hence for those interested in only capturing the keyboard events, would it not unnecessarily increase the workload?

Irfan.

Leave a comment

Comment on “Using the oninput event handler with onkeyup/onkeydown as its fallback”

Your input will be parsed as Markdown.