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,
		oEventData;

	// 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() {
		clearTimeout(iTimeoutId);
		fn.apply(this, arguments);
	}

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

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

	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.

Merge Sort

Continuing to evaluate efficient sorting algorithms, today we’ll look at merge sort. Merge sort[1] is a comparison sort using a divide and conquer algorithm, developed by John von Neumann[2] in 1945. It recursively divides the list into smaller sublists of length one, then repeatedly merges the sublists in order until there is only one sublist left. It has a worst case runtime of (O(nlogn)), making it worst-case more efficient than Quicksort.

Quicksort

We’ve looked a variety of in-efficient sorting algorithms, today we’ll look at Quicksort (aka. partition exchange sort), as a first foray into faster and more frequently used sorting algorithms. Quicksort[1] is a comparison sort using a divide and conquer algorithm, developed by Tony Hoare[2] in 1960. It recursively divides the list into smaller lists around a pivot value and sorts them, which means much smaller data sets when actually sorting. It has a ...

Applying Grayscale Using CSS Filters

This technique has been around for a while, but it’s powerful and worth sharing. Using the filter CSS property you can apply visual effects to your elements, including the grayscale we’ll be discussing here. For my CV I wanted my image muted most of the time, but pop when it becomes the focus of the viewer (ie. they mouse over it), so I used a filter to apply grayscale by default and remove grayscale ...

CSS Interview Questions

Lately, I have been interviewing many engineers who are interested in a CSS contractor position, and am thoroughly disheartened by the number of candidates who put CSS expert on their resume, but don’ even know the basics of CSS. This article will discuss the ten questions I usually ask, including the answer and why I ask the question. My hope is not to just give the answer, but to educate as well.

Questions

Each question ...

Hoisting 102 - Examining a Global Context Hoisting Gotcha

In an earlier article we covered Variable Hoisting in JavaScript. At the time, I did not expect to face a hoisting related code bug so quickly. And given the circumstances of the bug, I expect it to be one of the most common hoisting problems, so I wanted to share it.

How do it…

Let’s jump right into the setup. You have a function that is defined in one JavaScript file (this file ...

jQuery Widget for Dynamic Input Helper Text

This is a proof of concept widget that I demoed for work. The desire is to update some text according to a regex and replacement, when an input field changes. This will allow developers to show a message and/or format the input value, so users understand they do not need to enter meta characters and see the result of their input (think phone or identification numbers). I built a simple jQuery plugin that can be ...

Cross Browser Rounded Corners Using VML

Hopefully, your job does not have to support corporate customers whose IT departments do not keep the companies browsers up-to-date, and therefore do not need to support older version of IE. If however, like me, you need to support older IEs, then your companies designers have probably asked you to support rounded corners in IE. The three most common techniques to solve rounded corners are to use JavaScript [1] or an HTC Access[2] ...

Using jQuery Mobile With Django HTML5 Boilerplate

In the last article, I introduced the Django HTML5 Boilerplate package, which incorporates HTML5 Boilerplate into an easy to use Django package. Today’s article will take that one step further by showing how to use DH5BP to create a base template for your HTML 5 mobile app. We will be using jQuery mobile and the jQuery Widget for Showing Add To Home Message on iOS.

Getting ready

Install Django ...

Django HTML5 Boilerplate

I am a big fan of the HTML 5 Boilerplate project, and in that past, I have made some half-hearted attempts to incorporate it into a reusable Django module. I think the problem with my attempts and the other attempts that I have found recently on the web is that nobody automated the process of converting H5BP into a django module, so each time H5BP is updated (which happens frequently), somebody has to ...