jQuery plugin: Stylish Select Box – Unobtrusive select box replacement
I have released a new jQuery plugin that allows you to style the standard <select> form element with CSS so that it looks the same in all major browsers.
Checkout the latest source on GitHub
Download latest source code (version 0.4)
The plugin is available to download from the jQuery website.
Tested and works with:
Firefox 3.0, Firefox2.0, Chrome 3, Safari 4, IE8, IE7, IE6
Why?
Because sometimes the browser default form styles look ugly with the rest of your site and <select> elements are notoriously difficult to style with CSS. You can see how wildly different <select> elements can look in different browsers with the same CSS applied here.
The IE6 issue(s)
IE6 has a nasty habit of always displaying <select> elements on top of any other content, regardless of any z-index. This can be very annoying when using JavaScript overlays, drop down navigation etc. The only way around this issue is to use the iframe shim technique which places an iframe around the content that needs to appear above the <select>.
Then there’s that other issue of not being able to set a width on <select> elements without clipping off any options wider than the set width.
While workarounds do exist for these issues, I wanted to create a drop down menu that worked exactly the same in every browser and did away with these IE6 oddities.
So how does it work?
Stylish Select basically grabs all of the options from your select menu, puts them into an unordered list and hides the original <select>. This unordered list is then made interactive, so that it functions like a browser native <select>. When you click on or navigate through the list with the keyboard, it updates the <select> in the background, so that when you submit your form, it is just like you were using the original <select>.
This newly created <select> now has the flexibility to be styled in whatever way you choose.
Alot of work has been done to make the newly created <select> to function like the original browser default. The plugin offers the following features:
Supports keyboard navigation including alpha-numerical keys
If the <select> is at the bottom of the screen, the drop down menu stays on the screen
Support for grouped options with <optgroup>
If a value is selected, the new select menu shows this option as selected
You can invoke the plugin on any <optgroup> with the following method:
$(‘.my-dropdown’).sSelect();
Another question. How can I unbind the click event from the dropdown? I want it to have a ‘disabled’ state.
Hey Scott,
Is there a way have the dropdown list shown by default on page load?
Many thanks,
Jordan
Hello,
Great control.
The only thing – in IE6 and IE8 (did not test other IE vesrions) the behavior is a little odd.
The drop-down list shows when the arrow button or the text box is clicked, as it should; however, on the next click on the arrow button or the text box it hides and immediately shows again.
One needs to click outside the control to close the list without changing selection.
Firefox works correctly, click on the on the arrow button toggles the list.
Hello,
Here are some corrections for the bug in IE.
line 179:
$containerDivText.click(function(){
if ($newUl.is(“:visible”)){
$containerDiv.blur();
//$newUl.hide();
//positionHideFix();
return false;
}
else {
$containerDiv.focus();
//show list
$newUl.slideDown(opts.animationSpeed);
positionFix();
//scroll list to selected item
$newUl.scrollTop($input.liOffsetTop);
}
});
line 208:
$newLi.click(function(e){
var $clickedLi = $(e.target);
//update counter
currentIndex = $newLi.index($clickedLi);
//remove all hilites, then add hilite to selected item
prevented = true;
navigateList(currentIndex);
$newUl.hide();
$containerDiv.css(‘position’,’static’);//ie
});
line 301:
case 27:
//$newUl.hide();
//positionHideFix();
$containerDiv.blur();
return false;
break;
line 357:
$containerDiv.blur(function(){
$(this).removeClass(‘newListSelFocus’);
//$newUl.hide();
$newUl.slideUp(“fast”);
positionHideFix();
});
Hopefully this will help. Tested under IE7/FF3.5
Regards,
Icarius.
Sorry I was wrong to block line 208. The new code:
$newLi.click(function(e){
var $clickedLi = $(e.target);
//update counter
currentIndex = $newLi.index($clickedLi);
//remove all hilites, then add hilite to selected item
prevented = true;
navigateList(currentIndex);
$containerDiv.blur();
//newUl.hide();
//$containerDiv.css(‘position’,’static’);//ie
});
Small change to the menu disappears correctly (tested in Firefox 3, IE6 and IE7):
$containerDiv.blur(function(){
$(this).removeClass(‘newListSelFocus’);
//$newUl.hide();
$newUl.fadeOut(“fast”);
//positionHideFix();
});
Thanks alot for your input, when I get the time I have several improvements I need to implement in the plugin.
Hi Scott,
Thanks for the plugin. FYI I’m getting a problem where on a site with Prototype present as well, it is interfering with observing the change attribute of other elements. I’ve had a look through the code and can’t spot the bug but I’ll keep you posted.
Ahha! The conflict with prototype was caused by this code:
//create cross-browser indexOf
Array.prototype.indexOf = function (obj, start) {
for (var i = (start || 0); i < this.length; i++) {
if (this[i] == obj) {
return i;
}
}
}
How do I get the value of the select but not the display value. Tried getSetSSValue() but it gets the display value
A cappella
getSetSSValue() gives the value “A cappella” instead of 1
I’ve tried it and it looks very promising, but it has a couple of problems for me :
- When the list is too long, I don’t get an scrollbar.
- When using a getJSON to populate it, it isn’t posible, because it can’t reloaded.
Hi Scott,
This is absolutely fantastic. This is the easiest and the best solution to use out there. I have already implemented this in my website.
I have done a minor change (dunno, if it is already there in comments).
I have added ‘value’ to resetSS function.
Scenario: onChange event of a selectbox was filling another selectbox with values using AJAX. To control the height of this selectbox with ‘new’ values, I needed to call resetSS function with ddMaxHeight.
resetSS: function(value){
$this = $(this);
$this.next().remove();
//unbind all events and redraw
if(value)
$this.unbind().sSelect(value);
else
$this.unbind().sSelect();
}
Thanks alot for this plugin!
And for a select multiple, there is a solution?
So I’d like to not set a fixed width as I have multiple select options on one page. How could i go about getting the original select with and applying it to the ul?
[...] jQuery plugin – stylish select box replacement – Link. [...]
Scott, this is a very nice plug-in. Any thoughts of licensing it with the MIT license like jQuery itself? GPL 3 is rather restrictive.
You might want to change the following in your code:
tabindex=”0″
to:
tabindex=”‘+($(this).attr(‘tabindex’)!=’undefined’?$(this).attr(‘tabindex’):0)+’”
in order to keep tabindex as should be.
Great plugin!
nice plugin, I can’t use it though as the GPL is too strict. MIT would be perfect, same license as jquery which seems correct for a plugin, you do have terrible IE6 background image flicker problem on the demo page. the fix is to exec BackgroundImageCache setting. see http://ajaxian.com/archives/no-more-ie6-background-flicker
Hi!
Thank you for the plugin!
/Robin
same problem of Michael
Array.prototype.indexOf = function (obj, start) {
for (var i = (start || 0); i < this.length; i++) {
if (this[i] == obj) {
return i;
}
}
};
causes conflicts with prototype+jquery in noConflict() mode
@Michael
http://stackoverflow.com/questions/1585207/jquery-prototype-in-noconflict-mode-array-prototype-indexof-broken
just rename the function indexOf
@Dinesh:
If your original form select element had an ID of “country”, you’d just call $(‘#country’).val()
sSelect is already updating the hidden form element in the background, so there’s no use in reinventing the wheel.
I like the plugin, however, while it works fine in Firefox, in IE 6.0 if the select is inside of a jquery ui dialog, it will not display properly. What happens is the select will only expand up to the size of the ui_dialog. Is there a way to override jquery UI from allowing the select to expand beyond the container’s height?
the js code is:
$(‘#selTest’).sSelect();
// Dialog
$(‘#dialog’).dialog({
autoOpen: false,
width: 600,
buttons: {
“Ok”: function() { $(this).dialog(“close”);
},
“Cancel”: function() {
$(this).dialog(“close”);
}
}
});
// Dialog Link
$(‘#dialog_link’).click(function(){
$(‘#dialog’).dialog(‘open’);
return false;
});
the html:
Open Dialog
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
— Select One –
data1
data2
data3
data1
data2
data6
data1
data1
data2
data6
data1
Hi Scott,
thank you for your plugin. Great work!
If you’re interested, I managed to fix the odd behaviour in IE (drop-down list hiding and showing immediately after when you click on the control to close it) by using the toggle event instead of click:
$containerDivText.toggle(
function () {
$containerDiv.focus();
//show list
$newUl.slideDown(opts.animationSpeed);
positionFix();
//scroll list to selected item
$newUl.scrollTop($input.liOffsetTop);
},
function () {
$newUl.hide();
positionHideFix();
}
);
this is realy a great plugin and i want to use it for a project – however, i have a small problem with it..
it sets the height of my to 3px height
did anyone else experience this problem and can help me out?
comments cuted my html:
it sets the height of my “ul .newList” to 3px height
your select box really helped me out of a jam. nice implementation. very well designed. thanks alot!
Hi Guys,
I am having a problem in IE with this plugin:
Check it out here:
http://www.trendmicroaffinity.com.au/wash/form/test-id
Try selecting one of the items – it just won’t work.
Can anyone help me out here?
Thank you so much.
It’s okay guys, i forgot to define the variable json in my code, so just added var json and it works.
Thanks fantastic plugin!
why you added this lines +3 pixel in your script?
var newUlHeight = $newUl.height()+3,
containerHeight = $containerDiv.height()+3,
i removed this and added the following in the css script:
ul.newList { margin: 1px 0 0 -1px; ….
now the dropdown is aligned correctly and the height of the “dropdown-area” is also correct at the bottom.
maybe this changes are not correct with some of your settings, ill see
first of all, thanks a lot for this amazing plugin
is there a way please to animate the opening of the list?? i tried with the parameter animationSpeed but didnt work!
Thanks
hi,
i want to rectify my post
is there a way to animate the CLOSING of the list??
it would have been nice if the text of the dropdown list could fade in/out when we open/close the list!!
Cheers
Hi!
Your script is very useful
I used in an ecommerce and when i needed to colorize differently option list, i modified the script:
if ($input.children(‘optgroup’).length == 0){
$input.children().each(function(i){
var option = $(this).text();
// ADDED CSS_CLASS FOR TAKE CSS CLASS OF OPTION TAG AND PUT IN LIST ELEMENT
var css_class = $(this).attr(‘class’);
//add first letter of each word to array
keys.push(option.charAt(0).toLowerCase());
if ($(this).attr(’selected’) == true){
opts.defaultText = option;
currentIndex = i;
}
newListItems += ”+option+”;
});
//add new list items to ul
$newUl.html(newListItems);
newListItems = ”;
//cache list items object
var $newLi = $newUl.children();
}
This way permit to give a different class for every element.
Hope this is useful for the next update
Ciao
-dan-
PS: I’m sorry for my bad english
Interesting error. When I’m viewing this particular web page
(i.e. http://www.scottdarby.com/2009/05/jquery-plugin-stylish-select-unobstrusive-select-box-replacement/)
with Firefox 3.5.5 and I go to the Tools menu and select “Error Console” I get the following warning in the console repeated every 3 seconds or so:
Unknown pseudo-class or pseudo-element ‘first’.
Source File: http://www.scottdarby.com/2009/05/jquery-plugin-stylish-select-unobstrusive-select-box-replacement/
I don’t have any other pages open.
The plugin fails on multiple nested optgroups .i.e. when you add an optgroup within an optgroup they are formatted as if on the same level. I guess this is so rarely used, but I assume this would be easy to fix, i.e. a within a ….
I just wanted to thank you for this plugin–it’s exactly what I needed!
The indexOf function causes problems when using the library with the Drupal WYSIWYG module with TinyMCE. Removing it appeared to be the only thing that worked. I didn’t have the time to track down the exact reasons, but simply including the sSelect JScript file killed JScript in all versions of IE when parsing the TinyMCE JScript files.
[...] Stylish Select Box http://www.scottdarby.com/2009/05/jquery-plugin-stylish-select-unobstrusive-select-box-replacement/ [...]
Firstly, thanks for an awesome script. The IE truncation was killing me.
One minor thing I fixed:
In the “win-xp” style example, when a wrapped line of text is selected, the ascenders of the characters on the wrapped line appear in the couple pixels between the bar and the list.
I changed:
#win-xp .newListSelected div{
…to include an expressed height and hidden overflow:
height:14px; overflow:hidden;
Anthony
Hi,
Nice plugin. I have a problem with using ajax. May be coz i am new with jqurey. i want to auto populate the city dropdown as per selected country. my code is
$(function(){
$(‘#City’).sSelect();
$(‘#Country’).sSelect().change(function(){
$.getJSON(“select.php”,{id: $(this).val(), ajax: ‘true’}, function(j){
var options = ”;
for (var i = 0; i < j.length; i++) {
options += '’ + j[i].optionDisplay + ”;
}
$(“#City”).html(options);
});
});
})
Advance Thanks for help.
…I’m listing alot of data from sql and I wonder if anyone has a solution for scrolling the list?
Warning, a bug appears if your ‘text’ content for options have spaces (sting not trimed). onchange event on select will return ‘null’ value insthead of good value.
Nice! Very nice, BUT:
It seems that StylishSelect has a problem with ResetSS – after calling ResetSS all previously set custom handlers (especially for “change” event) are lost (due to .unbind() call in ResetSS) and are need to be bound again. I think it is not a right behavior. And unfortuantely I can’t imagine a simple solution. Keep seeking…
Hi again!
I’ve patched the SS. So now it keeps original events also after resetSS().
Unfortunately it keeps only those handlers that were bound before sSelect() has been applied. All handlers bound after sSelect() are destroyed after resetSS();
Patched version can be downloaded from:
http://www.artazor.lv/stylish-select/jquery.ss.js
Hi again! I’ve also JS-Lint’ed the code by http://www.jslint.com/
So now it is fully JS-Lint Quality complaint.
Seems working Ok, but I think complete unit-test suite would be necessary to publish it.
Now it lives at http://www.artazor.lv/stylish-select/jquery.ss.jslinted.js
Thank you again for amazing control.
Thanks for a nice bit of code,
I’m probably missing a trick here, but I cant seem to get the form to jump on an onchange event to an href location.
Im trying to do it with this javascript:
// javascript – jump menu
function MM_jumpMenu(targ,selObj,restore){ //v3.0
eval(targ+”.location=’”+selObj.options[selObj.selectedIndex].value+”‘”);
if (restore) selObj.selectedIndex=0;
}
Any help with be much appreciated,
thanks
@Mandemic:
That function is really more than what you need since you already have jQuery loaded. Here’s something that you could replace it with:
$(‘#my-select-element’).change(function(e) {
e.preventDefault();
window.location = $(this).val();
});
If you’re applying the Change event handler inline with your HTML, it would look like this:
First Option
If you’re changing the URL of a separate browser window or frame, you would need to replace “window” with whatever the window/frame name is.
Looks like the blog ate my HTML . . . here’s how it should look in pseudo code (just replacing the carats with square brackets):
[select id="my-select-element" onchange="window.location = $(this).val();"]
[option]Jump To[/option]
[option value="http://your-jump-link-url.com/link/path/"]Website #1[/option]
//…SNIP…//
[/select]
(For the record, I definitely prefer setting event handlers in JavaScript so that my script can be cleanly separated from the HTML.)
This plugin also doesn’t work correctly anymore with the new jQuery 1.4. When the option is changed and thus the value is changed $(element).val() doesn’t show the correct value anymore…
GOOD NEWS for everyone that’s using the new jQuery 1.4 together with this great plugin.
As you can read in my post above this plugin cannot get the correct value anymore by accessing it with $(element).val() as from jQuery 1.4.
As you can read on http://jquery14.com/day-01#backwards the jQuery team have adjusted the .val() function to get or set the selected option value instead of the selected text value.
HERE is the FIX:
Replace line 247:
$input.val(text).change();
WITH:
setinputvalue = $input.children().eq(currentIndex).val(); //Added for jQuery 1.4 compatibility
$input.val(setinputvalue); //Adjusted for jQuery 1.4 compatibility
Finally good to know that this adjustment is also compatible with jQuery 1.3!
I did not very rough testing yet but if I find issues I’ll let it know here…
Scott: maybe you can adjust your plugin with my adjustments so it works also with jQuery 1.4.
I also did two cosmetic inprovements to correct some issues when you use a dropdown icon with a hover effect so that the image recovers back to original when mouse is out of dropdownlist, these adjustments are:
Add under line 183 (positionHideFix();):
$(“.newListSelected”).removeClass(‘newListSelFocus’); //added by Jarno de Haan
Add under line 216 ($containerDiv.css(‘position’,’static’);//ie):
$(“.newListSelected”).removeClass(‘newListSelFocus’); //added by Jarno de Haan
Let me know if you have any questions or comments….