jQuery plugin: Stylish Select Box – Unobtrusive select box replacement

May 28th, 2009. Posted in CSS, Plugins, jQuery

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.

View a working demonstration.

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();

Working demonstration.

Bookmark and Share

111 Responses to “jQuery plugin: Stylish Select Box – Unobtrusive select box replacement”

  1. tersyxus says:

    Another question. How can I unbind the click event from the dropdown? I want it to have a ‘disabled’ state.

  2. Jordan Moore says:

    Hey Scott,

    Is there a way have the dropdown list shown by default on page load?

    Many thanks,
    Jordan

  3. Yuri Kyrpach says:

    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.

  4. icarius says:

    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.

  5. icarius says:

    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
    });

  6. icarius says:

    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();
    });

  7. Scott says:

    Thanks alot for your input, when I get the time I have several improvements I need to implement in the plugin.

  8. Michael says:

    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.

  9. Michael says:

    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;
    }
    }
    }

  10. Dinesh Sharma says:

    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

  11. Bert Heikamp says:

    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.

  12. Rahul says:

    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!

  13. neime says:

    And for a select multiple, there is a solution?

  14. N8 says:

    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?

  15. [...] jQuery plugin – stylish select box replacement – Link. [...]

  16. Kyle says:

    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.

  17. Klaas says:

    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! :)

  18. davey says:

    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

  19. Hi!
    Thank you for the plugin!

    /Robin

  20. Davide says:

    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

  21. Justin says:

    @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.

  22. Kat says:

    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

  23. Jordi says:

    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();
    }
    );

  24. blub says:

    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?

  25. blub says:

    comments cuted my html:

    it sets the height of my “ul .newList” to 3px height

  26. eric s says:

    your select box really helped me out of a jam. nice implementation. very well designed. thanks alot!

  27. Jason says:

    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.

  28. Jason says:

    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!

  29. dummy says:

    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 ;)

  30. Reda says:

    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

  31. Reda says:

    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

  32. -dan- says:

    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 :P

  33. Bill Krueger says:

    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.

  34. Ash Ward says:

    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 ….

  35. Amy says:

    I just wanted to thank you for this plugin–it’s exactly what I needed!

  36. Alan D. says:

    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.

  37. anthonydpaul says:

    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

  38. Riz Butt says:

    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.

  39. Nick says:

    …I’m listing alot of data from sql and I wonder if anyone has a solution for scrolling the list?

  40. Metal3d says:

    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.

  41. Artazor says:

    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…

  42. Artazor says:

    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

  43. Artazor says:

    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.

  44. mandemic says:

    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

  45. Justin says:

    @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.

  46. Justin says:

    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.)

  47. Jarno says:

    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…

  48. Jarno says:

    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….

Recent Posts

Categories

Archives