![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
The project of the day was making
select
/option
/optgroup
elements use jQuery UI's Selectmenu. This was a lot harder than I expected, mainly for cosmetic reasons. First, the drop down indicator (usually a downward pointing triangle) was too high and to the left, so it was overlapping text. The text also appeared to be much larger than the text in the original select
element, so the jQuery UI version would always underestimate the required width. After a lot of restyling, I had a basic, working select
element, but I didn't want it to be merely basic; if I did, I would've stuck with the regular one. Before Firefox moved to GTK3 on Linux, option
elements could be styled. In several places, I use color as an additional indicator, for example, there's a status indicator that can be either green or yellow. (There used to be a red, but that was removed. There's a text equivalent for those who are color-blind, but for those who aren't, it may be easier to just pick the color.) There's also a theme selector that shows the dominant colors of each theme. None of that is visible in Firefox on Linux. At first, I repurposed some of the demo source code, but I found that didn't work correctly, so I wrote my own. To allow the style
, class
, and title
attributes of the option
elements to become part of the li
elements that make up the Selectmenu, I customized the _renderItem extension point. To allow the drop down to fade in, I hooked the _resizeMenu extension point, since that's the closest extension point to where it starts showing the drop down. (I couldn't get it to fade out when closed and the slide down effect doesn't work very well if there are a lot of items.) The code uses a regular event to make sure the currently selected item is visible when the menu drops down and uses a loop to make sure that each select
element's title becomes a part of the new Selectmenu. Here's the code I wrote, which can be dropped into any JavaScript file assuming you've already loaded jQuery and jQuery UI. It only runs if a drop-down SELECT is on the page. I added lots of comments for its display here so you can see what I did and why; the version I'm running doesn't have any comments. If this code helps you, let me know; this site has an internal messaging system. With this code done, it's time to tweak my CSS.if ($("select:not([multiple])").length > 0) {
$.widget("custom.styledselectmenu", $.ui.selectmenu, {
// Customized to allow STYLE, CLASS, and TITLE attributes of the
// OPTION elements to become a part of the new LI element.
_renderItem: function (ul, item) {
return $("<li>", {
text: item.label,
style: item.element.attr("style"),
"class": (item.disabled ? "ui-state-disabled " : "") +
item.element.attr("class"),
title: item.element.attr("title")
} ).appendTo(ul);
},
// We want to fade in here, but to do that, we have to manually size
// the menu.
_resizeMenu: function () {
this.menu.width(
$("#" + this.menu.attr("aria-labelledby")).width()
).show();
// If we use jQuery UI effects, it shows up in the wrong place.
// slideDown doesn't work if the SELECT has a lot of options.
this.menuWrap.hide().fadeIn(200);
}
} );
$("select:not([multiple])").styledselectmenu( {
open: function (ev, ui) {
// When the menu drops down, make sure the currently selected item
// is visible. This is only useful if your SELECTs have a lot of
// items.
var x = $(".ui-menu .ui-state-focus");
if (x.length > 0) {
x[0].scrollIntoView(true);
}
},
close: function (ev, ui) {
// If we don't hide the menu at this point, it stays open because
// we faded in.
$("#" + ev.target.id + "-menu").hide();
},
// Default positioning doesn't work well if the selectmenu is at the
// bottom of a page. Fixed with collision: "flip"
position: { my: "left top", at: "left bottom", collision: "flip" }
} );
// Make sure the TITLE attributes on SELECT elements make it to the
// selectMenu version.
$('span[role="combobox"]').each( function (i) {
var x = $(this).attr("id")
if (x.length < 7) {
return;
}
x = "#" + x.substr(0, x.length - 7);
$(this).attr("title", $(x).attr("title"));
} );
}