jQuery Function for Change Event and Delayed Keydown Event

In my experience, it is rare to assign only a change event to a text input, as any callback that would be executed for the change event should also be called on a key event as well, but with a slight delay (think how an autocomplete shows results as you type). This is a common pattern and I was surprised to not immediately find a jQuery plugin implementing it, so I decided to add one myself.

How do it…

The jQuery plugin is available at http://plugins.jquery.com/changeOrDelayedKeyListener/.

Here is the basic way to use it (just like other jQuery events):

$('someSelector').changeOrDelayedKey(function(e) {
    // your event callback

You can pass a data object as the first argument, like other jQuery events:

$('someSelector').changeOrDelayedKey({/* a data object*/}, function(e) {
    // your event callback
    // e.data is the data object

There are two optional arguments for specifying the delay and the key event to use (keydown is used by default):

$('someSelector').changeOrDelayedKey(function(e) {
    // your event callback
}, 400, 'keyup');

And here is the code:

$.fn.changeOrDelayedKey = function(fn, iKeyDelay, sKeyEvent) {
	var iTimeoutId,

	// second signature used, update the variables
	if (!$.isFunction(fn)) {
		oEventData = arguments[0];
		fn = arguments[1];
		iKeyDelay = arguments[2];
		sKeyEvent = arguments[3];

	if (!iKeyDelay || 0 > iKeyDelay) {
		iKeyDelay = 500;

	if (!sKeyEvent || !this[sKeyEvent]) {
		sKeyEvent = 'keydown';

	// non-delayed event callback, should clear any timeouts, then
	// call the original callback function
	function fnExecCallback() {
		fn.apply(this, arguments);

	// delayed event callback, should call the non-delayed callback
	// after a short interval
	function fnDelayCallback() {
		var that = this,
			args = arguments;
		iTimeoutId = setTimeout(function() {
			fnExecCallback.apply(that, args);
		}, iKeyDelay);

	if (oEventData) {
		this.change(oEventData, fnExecCallback);
		this[sKeyEvent](oEventData, fnDelayCallback);
	else {

	return this;

The following example shows the event callback (the delay has been increased to 1000ms so it is easier to trigger the change event):

See the Pen jQuery on Change or Delayed Key Event Listener by Matt Snider (@mattsnider) on CodePen.

How it works…

The changeOrDelayedKey function has two signatures (this is common with jQuery functions). The first signature is a required callback function, an optional integer in milliseconds to delay before firing the callback on key events, and an optional string name of the key event to target. By default the key delay is 500 milliseconds and the keydown event is used. The second signature has an object, followed by the callback function, and then both optional arguments. And like most jQuery functions, changeOrDelayedKey is chainable.

Looking under the hood, the implementation is fairly simple. The changeOrDelayedKey function first figures out which signature was sent, assigning the correct values to local variables, and sets any defaults. There are two inner functions, the fnExecCallback simply clears any existing timeout and calls the original callback function, while fnDelayCallback waits the delay time before calling fnExecCallback. Some implementations my avoid the extra fnExecCallback, but I want to make sure that the internal timeout is always cleared before delegating to the provided callback functions. Lastly, the fnExecCallback function is assigned to the change event and the fnDelayCallback function is assigned to the desired key event.

There’s more…

There are two caveat when using the changeOrDelayedKey function.

The first is that it doesn’t work well with the jQuery event off function for unsubscribing from events. You can use the off function to remove all change and keydown events, but you can’t remove just the callback function that you provided, because the actual callback function passed into change and keydown listeners is an anonymous function. I don’t foresee a big demand for this feature, so I haven’t included it.

The second is that I haven’t wired it into the jQuery event on function. The signature for the on function adds more complexity that I did not want to support unless absolutely necessary. And since listeners can be called directly, I don’t see a need yet for supporting the on function.