In selectList version 0.5, I made use of an interesting feature of jQuery, called valHooks. It’s used internally in jQuery (since version 1.6) and there’s not much documentation on it, but it can be very useful for plugin authors.
Valhooks allow you to change the way the jQuery method .val()
works for certain elements, giving you control over how element values are set and retrieved. You define a function and assign it to a specific element type, and whenever .val()
is called on that element type, your function is invoked to process the call.
Let’s take a look at an example — the code below is a very simple jQuery plugin that makes use of a get
valhook to convert the value of an input field before it is retrieved. In practical terms, it allows you to enter an expression representing the number of bytes, with the ability to parse quantifiers like “kB” for kilobytes or “MB” for megabytes, and automatically translate them into bytes. So, “1k” or “1kB” becomes “1024″, “1M” or “1MB” becomes “1048576″, and so on.
(function ($) {
// jQuery plugin definition
$.fn.bytesInput = function () {
$(this).filter('input[type="text"]').each(function() {
$(this).data('bytesInput', true);
});
return this;
};
var origHook;
// There might already be valhooks for the "text" type
if ($.valHooks.text)
// Preserve the original valhook function
origHook = $.valHooks.text.get;
else
// Make room for a new valhook
$.valHooks.text = {};
$.valHooks.text.get = function (el) {
// Does this element have our data field?
if (!$(el).data('bytesInput'))
// No -- check if there was a valhook function already defined
if (origHook)
// There was, so go ahead and call it
return origHook(el);
else
// No previous function, return undefined to have jQuery
// take care of retrieving the value
return undefined;
// Try parsing the expression
if (matches = el.value.match(/(\d+)\s*([kKmMgG]?)[bB]?/)) {
switch (matches[2].toLowerCase()) {
case 'k':
// Kilobytes
return matches[1] * 1024;
case 'm':
// Megabytes
return matches[1] * 1048576;
case 'g':
// Gigabytes
return matches[1] * 1073741824;
default:
// Just bytes
return matches[1];
}
}
else
// Can't parse the expression -- just return the current value
return el.value;
};
})(jQuery);
The example is pretty straightforward, and if you’re familiar with how jQuery plugins work, you should have no trouble understanding it. One specific thing that might be worth pointing out is how the .data()
method is used to distinguish the special input field from other inputs (for which the .val()
method should work the usual way). The plugin initialization function attaches data to the element at the "bytesInput"
key — the get valhook function then checks for the presence of that key, and only does its thing if the key is there. Otherwise, it either falls back to the original valhook function (if there was one), or returns undefined
, which makes jQuery process it in the traditional manner. This way the plugin can be used with other plugins or pieces of code that set their own valhooks for input elements, and not interfere with them.
If you’d like to see the above example in action, I put it up on jsFiddle.