Monday, October 29, 2012

jQuery UI selectmenu and the change event

Many moons ago, the Filament Group created a jQuery UI component called Selectmenu.

Then Felix Nagel forked it, and made improvements!

If you're using jQuery UI 1.8.X or older, look at the links from Felix Nagels forked version.  If you're using jQuery UI 1.10.X, it should be available officially within jQuery UI.  However, at the time of writing, jQuery UI 1.9 is what is in the wild, so you have to cherry pick from Github, and not quite get the full benefit of Themeroller.

Selectmenu has a few ways of doing things that are a bit counter intuitive to the way you'd expect things to be done.  This is mostly because it is made up of existing components.

At some point, to set the widget to an existing value in the select list, you needed to do this

$("#myselect").selectmenu("value", value);

However, these days you can get away with

$("#myselect").val(value);

And if you've altered the options on the select list, you may also want to follow up with

$("#myselect").selectmenu("refresh");

One of the counter intuitive actions about the component is handling of the change event.  With a native select element, you can do any of the following, depending on your jQuery version and preference:

$("#myselect").change(function() {});
$("#myselect").bind("change", function() {});
$("#myselect").on("change", function() {});

But given the Selectmenu component, as it stands, you need to do this:

$("#myselect").selectmenu({change: function() {}});

But this doesn't allow for event propagation.  So here's what I've started doing to allow event propagation, whenever I set up a Selectmenu instance.

var changeSelectMenu = function(event, item) {
    $(this).trigger('change', item);
};
$("#myselect").selectmenu({change:changeSelectMenu});

And now I can use any of the previous methods to implement change handling, and have multiple handlers for the event.

Caveat: I'm a scoping noob. Usage of $(this).trigger() may be bad. My intention is that it should target the element that the component is attached to.  Perhaps I should even be using the event to get the event name, rather than hard code 'change', and then I could call the function variable propagateEvent.  Maybe there is even a better way to propagate the original event, rather than start a new one.

I would rather that the component set this behaviour up by default, but there's probably something intrinsically wrong about it that made it seem like a bad idea.