Introducing jQuery SpicySelect

I recently worked on a project in which the client wanted to have custom styled select boxes. Long story short, I found an obscure jQuery plugin that claimed to do it but was not compatible with Rails at all and was missing some functionality, like the support of option groups, and did not perform well on large lists. Needless to say, I completely reengineered the plugin and re-released it on github under a different name, SpicySelect. To use it, all you need to do is call on a normal select written out by your app:



$("select").spicyselect();


The plugin will hide your select and write out a DOM structure of ol/li elements wrapped in a div. It will carry over any classes you had on your select or options and wire up the events so that the mask works similarly to the underlying select. Any time the mask's selected option is changed, the underlying select's option is also changed and the change event is fired. That allows you to write code keying off the select's change event like you normally would and the mask will not interfere.

I put a demo up, although it has not been styled yet. I am hoping to get that done over the course of the next week or so. I also published a quick link to the spec.

Pertinent URLs:

github Project Page

Demo

Spec

I hope you find the plugin useful. If you have any comments, issues, feature requests, bugs, etc, please don't hesitate to email me!

A Better Default Value jQuery Plugin

Recently I forked Doug Neiner's inFieldLabels plugin to try and make it a little bit more automatic than it was, which has proved to be problematic. After having used my fork in a couple of sites, I have found that it is more trouble than it's worth to do the absolute positioning like I did. It specifically doesn't work well when things are moving on the screen, such as expanding hidden DOM elements, which cause the label to stay put while the form element moves. I thought about fixing it to detect those kinds of movements and handle some other edge cases that were reported to me, but I felt like the plugin was going to get way more complicated than it needed to be to accomplish a seemingly simple task. I was very happy to find Jan Jarfalk's Defaultvalue plugin, which is a much simpler implementation and works like a champ. No special styling or DOM elements required. It accomplished what I wanted to do in a much more elegant manner. I have successfully replaced inFieldLabels with it on my projects and I am no longer experiencing any of the styling or positioning hassles. There was one quirk with it though. Since it actually fills in an input field with the default value, if that value is present in the field when it posts, that value will be sent to the server. I made a minor update to it to account for this and submitted it back to the author. I hope he incorporates it into his plugin, but we'll see. In the meantime, you can patch it by chaining in the following code to line 37 right after the call to focus, which will cause it to clear the default upon form submit if it is still present in the field. [js] //end of focus event }).closest("form").submit(function() { self.val() == defaultValue && self.val(''); }); [/js] There is some additional nit-picky cleanup that could be done in the plugin as well, but overall it looks, and more importantly works, very good. Kudos to Jan for putting it together and sharing it!

Using ".call" in JavaScript

Every so often I find myself binding a function to an event, but I want to also run that function on page load. Sometimes you can just invoke the event right away, like this: [js] $(".some_element").change(function() { //do something }).change(); [/js] That can have side effects that you don't want though, such as executing other functions unintentionally. In that case, you can name the function and call it explicitly after the bind, like this: [js] function doSomething() { //do something } $(".some_element").change(doSomething); doSomething(); [/js] That has another problem though. If you were using "this" and expecting it to be the changed element (which you probably were), then that context won't be set. Luckily, there is an easy way to get around that as well. You can invoke the function in a different way and set the context to be a specific DOM element, just like jQuery itself does. [js] $(".some_element").change(doSomething).each(function() { doSomething.call(this); }); [/js] This way, you can write the function assuming the context of this like you normally would and still directly invoke it as need be.

Are you using jQuery.extend properly?

jQuery's extend function is pretty powerful for merging objects and it is the hinge that plugins rely on to operate. It is very easy to use, but if you don't understand exactly what it does, it can come back and bite you. The first case for using the method is to extend the jQuery object itself. Calling it on jQuery or jQuery.fn will add functions to the jQuery namespace or jQuery objects, respectively. The second case is to merge objects together. You can do this by calling extend with multiple parameters. The first parameter is considered to be the base and all subsequent ones are merged into it in order. The updated object is also returned. The important thing to be aware of is that the first parameter will be updated by this call. For example, if you have an object in your plugin that has its defaults stored in an object and you pass that in as the first argument, then it will by updated by the parameters passed in. The wrong way to do it, causing your defaults to be rewritten: [js] var defaults = { some: 'option' }; function myPlugin(user_options) { var options = $.extend(defaults, user_options); ... } [/js] Instead, the safe way to call extend in that instance is like this: [js] var options = $.extend({}, defaults, user_options); [/js] That will cause the user options to override the defaults without rewriting them and store it in another variable for your use.