type: widget
release: 0.0
status: in planning
documentation: url || N/A
demo: http://jquery-ui.googlecode.com/svn/branches/labs/selectmenu/index.html
1 - Description:
A jQuery UI widget that duplicates and extends the functionality of a native HTML select element, allowing it to be customizable in behavior and appearance far beyond the limitations of a native select.
2 - Visual Design:

3 - Functional Specifications/Requirements:
The default way to use this plugin is by calling the .selectmenu method on a select element:
$('select').selectmenu();
Feature details:
- selectmenu will act as a proxy back to the original select element, controlling its state for form submission or serialization
- If the hideElement option is true, the original select element will be set to display: none after selectmenu is created. The original select menu will not be removed from the page because it will retain the widget state for form submission.
- Any form label originally associated with the select menu will be re-associated with the new form control. Clicking it will focus the new selectmenu.
- The menu will support both in-place "popup" or drop-down styles
- The menu will be fully keyboard accessible using matching conventions to native select elements
- Menu is keyboard accessible while closed as well - arrow keys make different selections
- The menu will have ARIA support for screenreaders so the original select element can safely be hidden from all users
- Different arrow icons depending on menu type
- The format option accepts a function with a single argument that can be manipulated and returned with any level of manipulation you'd like.
Still needs to be built:
- collision detection and stackfix / iframe
- disabled method doesn't truly disable widget
Options:
- transferClasses: true, transfer classes from select to both button and menu container (option classes are always transferred)
- style: 'popup', also avail: 'dropdown'
- width: null, defaults to select width, accepts a number
- maxHeight: null, sets the maximum height for the menu body
- menuWidth: null, sets menu body separately. defaults to width option value, accepts a number
- handleWidth: 26, number width that the icon arrow block will hang off the edge in a 'popup' style menu
- icons: null, array of objects with "find" and "icon" properties. "find" is a jQuery selector, "icon" is a jQuery UI framework icon class ("ui-icon-script").
- format: null, array of objects with find and rep properties. Each will run in order and perform find/replace formatting. For example: format: [{find:/^([a-zA-Z0-9 ]+)\-/g, rep: '<span class="header">$1</span>'} will find all text before a " - " and wrap it with a span.header.
Callbacks/Events: (each returns the event as first arg, second arg is the ui hash, which currently contains properties: "value")
- Open
- Close
- Change
- Select
- Value
4 - Markup & Style:
4.1 Initial markup examples
<select name="speedA" id="speedA">
<option value="Slower" class="whoo">Slower</option>
<option value="Slow">Slow</option>
<option value="Med" selected="selected">Med</option>
<option value="Fast">Fast</option>
<option value="Faster">Faster</option>
</select>
4.2 Recommended transformed HTML markup
<a aria-owns="speedA_menu_318" aria-haspopup="true" href="#" id="speedA_button_318" class="ui-selectmenu ui-selectmenu-popup ui-widget ui-state-default ui-corner-all" tabindex="0" style="width: 173px;">
<span class="ui-selectmenu-status">Slower</span>
<span class="ui-selectmenu-icon ui-icon ui-icon-triangle-2-n-s"></span>
</a>
<!-- at end of body-->
<ul id="speedA_menu_318" role="menu" aria-labelledby="speedA_button_318" class="ui-selectmenu-menu ui-widget ui-widget-content ui-corner-all ui-selectmenu-menu-popup ui-selectmenu-open" style="width: 147px; left: 184.017px; top: 136.95px;">
<li class="whoo ui-corner-top ui-selectmenu-item-selected ui-state-active"><a aria-selected="true" role="option" tabindex="-1" href="#">Slower</a></li>
<li><a aria-selected="false" role="option" tabindex="-1" href="#">Slow</a></li>
<li><a aria-selected="false" role="option" tabindex="-1" href="#">Med</a></li>
<li><a aria-selected="false" role="option" tabindex="-1" href="#">Fast</a></li>
<li class="ui-corner-bottom"><a aria-selected="false" role="option" tabindex="-1" href="#">Faster</a></li>
</ul>
4.3 Accessibility recommendation
Attributes on button:
- aria-haspopup="true"
- aria-owns="[widget menu's id value]"
- generated ID for ARIA element referencing
- tabindex of 0 or value from select element
- aria-disabled (accordingly)
ARIA attributes on menu portion:
- role="menu"
- aria-labelledby="[widget button's id value]"
- generated ID for ARIA element referencing
- Each anchor within menu requires :
- aria-selected="true/false"
- tabindex="-1"
- role="option"
4.4 CSS & Theme
/* Selectmenu
----------------------------------*/
.ui-selectmenu { display: block; position:relative; height:2em; text-decoration: none; overflow:hidden;}
.ui-selectmenu-icon { position:absolute; right:6px; margin-top:-8px; top: 50%; }
.ui-selectmenu-menu { padding:0; margin:0; list-style:none; position:absolute; top: 0; visibility: hidden; overflow: auto; }
.ui-selectmenu-open { visibility: visible; }
.ui-selectmenu-menu-popup { margin-top: -1px; }
.ui-selectmenu-menu-dropdown { }
.ui-selectmenu-menu li { padding:0; margin:0; display: block; border-top: 1px dotted transparent; border-bottom: 1px dotted transparent; border-right-width: 0 !important; border-left-width: 0 !important; font-weight: normal !important; }
.ui-selectmenu-menu li a,.ui-selectmenu-status {line-height: 1.4em; display:block; padding:.3em 1em; outline:none; text-decoration:none; }
.ui-selectmenu-menu li.ui-selectmenu-hasIcon a,
.ui-selectmenu-hasIcon .ui-selectmenu-status { padding-left: 20px; position: relative; margin-left: 5px; }
.ui-selectmenu-menu li .ui-icon, .ui-selectmenu-status .ui-icon { position: absolute; top: 1em; margin-top: -8px; left: 0; }
.ui-selectmenu-status { line-height: 1.4em; }
.ui-selectmenu-open li.ui-selectmenu-item-focus a { }
.ui-selectmenu-open li.ui-selectmenu-item-selected { }
.ui-selectmenu-menu li span,.ui-selectmenu-status span { display:block; margin-bottom: .2em; }
.ui-selectmenu-menu li .ui-selectmenu-item-header { font-weight: bold; }
.ui-selectmenu-menu li .ui-selectmenu-item-content { }
.ui-selectmenu-menu li .ui-selectmenu-item-footer { opacity: .8; }
/*for optgroups*/
.ui-selectmenu-menu .ui-selectmenu-group { font-size: 1em; }
.ui-selectmenu-menu .ui-selectmenu-group .ui-selectmenu-group-label { line-height: 1.4em; display:block; padding:.6em .5em 0; font-weight: bold; }
.ui-selectmenu-menu .ui-selectmenu-group ul { margin: 0; padding: 0; }
5 - Latest version of plugin:
6 - Open issues being discussed
- Should the enter key, when pressed on a focused-but-closed button, submit the parent form?
- Event callbacks arguments - what should they be?
- Should we have an option for shadow support on the menu?
- Should we have an option for scrollbarless slick scrolling (arrows at top and bottom and list scrolls dynamically with mouse position)?
- ARIA attributes currently in use have been questioned. Discussion here: http://groups.google.com/group/jquery-ui-dev/browse_thread/thread/f4f81a2d91761478
- Positioning of menu is a little off in the optgroups example.
7 - Related jQuery plugins
Comments (30)
Jörn Zaefferer said
at 12:22 pm on Jun 16, 2009
The value option doesn't make sense to me. The menu is built from a select, so the selects value is used, thats it.
Worked for autocomplete, too; no one ever requested a value option...
Autocomplete has a width option, which applies to the suggestion list. Should that be renamed to menuWidth for consistency?
Is handleWidth really something you need to configure via JS?
I don't get what the icons option does (by looking at the spec). An array doesn't have named properties, as that supposed to be an object? What does it do?
The format option is could be remodelled after the source/parse design for Autocomplete (http://wiki.jqueryui.com/Autocomplete). Let the user provide a callback for either the whole dataset or each row, and display the return value. Might be worth considering to make that asynchronous: Provide the data in the first argument, and a callback in the second. Let the user call that callback with the formatted data; that allows an ajax request to enhance the data to display, which would work best in combination with getting the full dataset as the first argument - you wouldn't want a request for each row. The plugin could still check the return value of the callback, if it isn't undefined, assume a synchrnous operation and use the return value, instead of waiting for the response-callback.
scottjehl said
at 1:08 pm on Jun 16, 2009
value option: Agreed. So should we just keep it as a getter/setter method?
-------------------------------------------------------------------------------------------------------------------
width option: sure!
-------------------------------------------------------------------------------------------------------------------
handleWidth : I think so - it really depends on the implementation and how your icon looks (which could be a custom image).
scottjehl said
at 1:08 pm on Jun 16, 2009
-------------------------------------------------------------------------------------------------------------------
icons option: so what this does is allows you to pass an array of objects, like this:
$('select').selectmenu({
icons: [
{find: '.script', icon: 'ui-icon-script'},
{find: '.image', icon: 'ui-icon-image'}
]
});
...which will add ui-icon-script icons to the li's with a class of ".script", and so on.
The find "property" is a jQuery selector, which is applied within the scope of the new menu UL. Any classes that were previously on options are carried over to the LI's, so classes work here, but you could also pass "li" to get every list item.
Passing a property at all means elements that meet that selector will have a <span class="ui-selectmenu-item-icon ui-icon"/> prepended, which is all you'd need for custom icon CSS.
The "icon" property is optional. It's an additional class name you can add to the span, such as ui-icon-script or ui-icon-arrow-1-e.
Do you have an idea for a better way to handle the icons option?
-------------------------------------------------------------------------------------------------------------------
format option: I agree that the two plugins should use the same system for this. I do think that the functionality already employed in this plugin is pretty useful though, and the regex it uses would put it out of reach for a large portion of the audience. If I switched the option to be a callback, do you think it would make sense to have it apply the formatting (or something like it) that we're demonstrating in the 3rd example above (see source code for detail).
scottjehl said
at 1:47 pm on Jun 18, 2009
I changed the format option to a callback with one option for now. This should be extended to support the functionality that autocomplete provides as well.
ajpiano said
at 2:33 pm on Jun 16, 2009
I have a feeling that people (like me) might also want to include images in the Selectmenu (and Autocomplete, fwiw) without creating CSS classes. Perhaps it would be easier to have the image/icon be part of the data that is transformed by the widget? Either an img or cssClass type of thing?
scottjehl said
at 3:18 pm on Jun 16, 2009
If you need to append content images to each LI, I think you could do that using the format option (assuming we set it up as a callback with some useful arguments, such as the li's class name, text content, etc).
That would separate the icons and format options by their purpose (visual enhancements vs. content)
Thoughts?
Jackson F. de A. Mafra said
at 11:25 am on Jul 13, 2009
How can i use to replace two dynamic selects?
Sam Critchley said
at 11:24 pm on Jul 28, 2009
Is there a way to dynamically modify the options in the original SELECT and have the widget reflect those changes? Or should I call destroy and recreate the 'selectmenu'?
Jonathan Protzenko said
at 9:43 am on Aug 4, 2009
Sam, you need to delete and the recreate the widget.
Apart from that, I notified Filament of some issues with this plugin on the original blog post. I thought I should write a link to it from here, for further reference.
http://www.filamentgroup.com/lab/jquery_ui_selectmenu_an_aria_accessible_plugin_for_styling_a_html_select/#commentNumber26
ajpiano said
at 10:17 am on Aug 5, 2009
The normal UI option setter syntax ...("option","foo","bar"); should work. If it does not currently (I haven't had a chance to test), then I am sure it will before it is released.
hugh.lomas said
at 11:09 am on Aug 18, 2009
The custom icon implementation needs some work.
Currently the icons are not dynamically sizable. CSS background-image will not scale an image to the height/width of the container, at least until CSS3, so the icon image files have to be an arbitrary static size.
If they were instead <img> elements, they would be scalable.
Could each option element use an optional custom attribute, like "icon-path" or something similar, and build the icon from that?
sompylasar said
at 6:20 am on Sep 6, 2009
Font families seem to be different in closed and opened states (the widget itself and the menu that pops up).
Line height for a popup with custom images also seems to be different on the select and the menu.
sompylasar said
at 6:52 am on Sep 6, 2009
Font families are the same but font-weight is different which leads to a noticeable jump in the width of text.
sompylasar said
at 6:33 am on Sep 6, 2009
Menu selected item isn't aligned vertically with the widget in popup with optgroups demo.
ajpiano said
at 5:08 pm on Sep 10, 2009
The format callback certainly doesn't match the one in the spec...not that it ought to. Format should be (and is) a callback, not a hash of find and replaces. The _formatText method ought to pass the text and the actual OPTION element to the format callback, instead of just the text. This allows for much more versatility in formatting the list items.
ajpiano said
at 11:54 am on Sep 13, 2009
There is also a bug in type-ahead that eats up ctrl+keypresses. Surprised no one ran into this, as it breaks ctrl-r!
ajpiano said
at 3:44 pm on Sep 16, 2009
There is no reason that maxHeight shouldn't be compatible with dropdown style, in my estimation. I was using maxHeight on a selectmenu, but it was getting jittery over the window edge. When i took out the if (style=="popup") in the mouseup that uses the timeout to delay for the safemouseup, the maxHeight works fine in both styles.
jacobstr said
at 7:09 pm on Sep 27, 2009
What's the hold up with this? This seems like a much more fundamental, and core widget than something like autocomplete (which I can build myself) and shadow (which I won't even use because the slow-down isn't worth the minor visual flair). Yet, it's rated as very low priority?!
I'm sure everyone here that has used jquery ui in a project has run into the situation where 90% of your widgets are looking nice and consistent. You can replace all your buttons and text inputs, with equivalent markup and jquery ui styles (ui-state-default). We don't need to do anything special for text inputs to achieve a similar look (background + border are sufficient here). The things you can't really style in a satisfying way without javascript are select menus and radio/checkbox buttons. The normal HTML widgets (as well as file uploads and fieldsets) are too wonky in their styling.
Not only is this way too low priority (IMHO), unlike some of the other proposals on here, this one is nearly complete. It's also a plugin that I would hate to write myself, without nice community to help support it . Transparently replacing HTML select widgets better be done properly, and when some random use case comes up where it's broken it's great to have lots of eyes on it to fix it. If your fancy select widget is broken in ie 7 and you can't change a parameter in the back end of your web app because of it, your users have a right to be PO'd.
Klaus Pesendorfer said
at 4:34 am on Dec 16, 2009
The optgroup labels do not work with spaces.
I've solved this problem with the fix below:
- use the label only to display and reduce the label to an id without spaces for all other statements
... maybe this could be improbed to remove all special characters that are not allowed as css-class-name
===================================================================
--- ui.selectmenu.js 2009-12-15 16:35:05 UTC (rev 5596)
+++ ui.selectmenu.js 2009-12-15 17:17:52 UTC (rev 5597)
@@ -122,7 +122,8 @@
text: self._formatText(jQuery(this).text()),
selected: $(this).attr('selected'),
classes: $(this).attr('class'),
- parentOptGroup: $(this).parent('optgroup').attr('label')
+ parentOptGroup: $(this).parent('optgroup').attr('label').replace(/\s/g, ""),
+ parentOptGroupLabel: $(this).parent('optgroup').attr('label')
});
});
@@ -165,7 +166,7 @@
this.list.find('li.' + optGroupName + ':last ul').append(thisLi);
}
else{
- $('<li class="'+self.widgetBaseClass+'-group '+optGroupName+'"><span class="'+self.widgetBaseClass+'-group-label">'+selectOptionData[i].parentOptGroup+'</span><ul></ul></li>')
+ $('<li class="'+self.widgetBaseClass+'-group
+'+optGroupName+'"><span
+class="'+self.widgetBaseClass+'-group-label">'+selectOptionData[i].pare
+ntOptGroupLabel+'</span><ul></ul></li>')
.appendTo(this.list)
.find('ul')
.append(thisLi);
Klaus Pesendorfer said
at 4:45 am on Dec 16, 2009
Is there a possibility to select an entry in the selectmenu by the option-value (<option value="123">label</option>) of the original select element and not by the index?
$('#mycombo').selectmenu('value', elemlIndex);
It would be nice to have such a method!
Wichert Akkerman said
at 10:39 am on Dec 23, 2009
I feel that the format function should be passed the option element in addition to (or perhaps even in place of) the text. Let me explain a use case for this: I am creating a select menu to select the brand of a product. For each brand I also want to show the logo in the select menu. The non-javascript markup looks like this:
<select>
<option style="background: url(/contentstore/123d1231.jpeg) no-repeat left" value="1">Adidas</option>
<option style="background: url(/contentstore/2341a112.jpeg) no-repeat left" value="1">Nike</option>
</select>
Firefox the logo will show the logo correctly in the select menu. Other browsers ignore the style on the <option> element. When using selectmenu on this markup I want to extract the background image URL in my format function and use that to create a <img> element. This is only possible if the format method gets the <option> element as parameter as well.
Scott González said
at 10:59 am on Dec 23, 2009
1
Wichert Akkerman said
at 6:52 am on Feb 23, 2010
Here is the changeset I used to implement this locally:
@@ -390,8 +393,8 @@ $.widget("ui.selectmenu", {
if(this.list.is('.'+ this.widgetBaseClass +'-open')){ this.close(event,retainFocus); }
else { this.open(event); }
},
- _formatText: function(text){
- return this.options.format ? this.options.format(text) : text;
+ _formatText: function(text, el){
+ return this.options.format ? this.options.format(text, el) : text;
},
_selectedIndex: function(){
return this.element[0].selectedIndex;
@@ -119,7 +119,7 @@ $.widget("ui.selectmenu", {
.each(function(){
selectOptionData.push({
value: $(this).attr('value'),
- text: self._formatText(jQuery(this).text()),
+ text: self._formatText(jQuery(this).text(), this),
selected: $(this).attr('selected'),
classes: $(this).attr('class'),
parentOptGroup: $(this).parent('optgroup').attr('label')
Wichert Akkerman said
at 6:39 am on Dec 24, 2009
The current implementation does not match the spec form this page: the value property in ui hash passed to events does not specify the value of the selected option, but the index of the selected option.
I am not sure if there is a need for a new ui hash data type; perhaps it would be simpler to just include the original <option> element? That already has the value and any other data that you might want.
Nathan L Smith said
at 8:38 am on Jan 7, 2010
The plugin does not behave correctly in environments where Array.prototype is augmented. There's a patch here: http://dev.jqueryui.com/ticket/5034
Wichert Akkerman said
at 6:46 am on Feb 23, 2010
If you click on the menu to open it it is immediately closes again. This is different from a standard <select> which stays open until it looses focus. The only way to keep it open appears to be hold down your mousebutton and move the mouse down until you hover over an item, then move the mouse outside of the menu and release the button.
Wichert Akkerman said
at 6:48 am on Feb 23, 2010
The behaviour for a large list of options is suboptimal at the moment. The entire list can be (much) larger than the current page height, in which case the page is stretched to make room for the entire list, which adds a lot of empty space to the page. Selecting items at the end of the list is very awkward since there is no scroll option, and the menu often closes before you found the item you are looking for.
Wichert Akkerman said
at 6:50 am on Feb 23, 2010
I found it useful in event handlers to know the index if the selected option and the option element itself. I did this using a simple change:
@@ -339,8 +339,11 @@ $.widget("ui.selectmenu", {
this._prevChar[0] = C;
},
_uiHash: function(){
+ var index = this.value();
return {
- value: this.value()
+ index: index,
+ option: $("option", this.element).get(index),
+ value: this.element[0].value
};
},
open: function(event){
scottjehl said
at 11:29 am on Feb 23, 2010
This looks like a good idea to me, but the "option" property seems like it should be named a bit more specifically. selectedOption maybe? Also should index be selectedIndex?
Wichert Akkerman said
at 11:32 am on Feb 23, 2010
I have no strong opinion on the naming. selectedOption and selectedIndex work for me.
You don't have permission to comment on this page.