Reinventing a Drop Down with CSS and jQuery

July 28, 2009

For me, standard HTML Select element is pretty much annoying. It's ugly. It can't be styled properly in Internet Explorer. And it can't contain nothing but simple text. That is the reason why I needed to reinvent Drop Down element. This tutorial shows how to do that (easily, believe it or not).

View demo Download source code

There is an update at the end of this tutorial and the second demo that shows the results of this updated code.

Simple structure

Let me explain HTML structure that will be used here. In this example we will use a short list of 8 countries. List is created using Definition List (DL) element. Why this element? It is similar to unordered/ordered list – the only difference is that DL consists of two elements: DT (term) and DD (definition). This makes it perfect candidate for Drop Down element. Element DT can be used to show collapsed state with currently selected option while DD can show all the available options in nested UL. Here is the sample structure:

<dl class="dropdown">
    <dt><a href="#"><span>Please select the country</span></a></dt>
    <dd>
        <ul>
            <li><a href="#">Brazil</a></li>
            <li><a href="#">France</a></li>
            <li><a href="#">Germany</a></li>
            <li><a href="#">India</a></li>
            <li><a href="#">Japan</a></li>
            <li><a href="#">Serbia</a></li>
            <li><a href="#">United Kingdom</a></li>
            <li><a href="#">United States</a></li>
        </ul>
    </dd>
</dl>

In order to make Drop Down functional we have to add several important CSS styles. First of all we have to reset margins and paddings for DD, DT and UL. DD will be positioned relatively, so that nested UL can be absolutely positioned. As I mentioned earlier, in collapsed state, only DT will be visible. It contains link with span inside it and two elements make sliding doors, a technique that is often used for creating buttons and tabs. Styling UL is simple, it will be positioned 2px below DT and initially hidden.

/* General dropdown styles */       
.dropdown dd, .dropdown dt, .dropdown ul { margin:0px; padding:0px; }
.dropdown dd { position:relative; }
/* DT styles for sliding doors */
.dropdown dt a {background:#e4dfcb url(arrow.png) no-repeat scroll right center;
    display:block; padding-right:20px; border:1px solid #d4ca9a; width:150px;}
.dropdown dt a span {cursor:pointer; display:block; padding:5px;}
/* UL styles */
.dropdown dd ul { background:#e4dfcb none repeat scroll 0 0; display:none;
    list-style:none; padding:5px 0px; position:absolute; 
    left:0px; top:2px; width:auto; min-width:170px;}
.dropdown span.value { display:none;}
.dropdown dd ul li a { padding:5px; display:block;}

After adding some colors, borders and hover effects (check out source code on demo page), the result so far will be something that really looks like Drop Down.

Let's make it work

It's time to involve jQuery. Each click on DT (actually link inside DT) will toggle nested UL.

$(".dropdown dt a").click(function() {
    $(".dropdown dd ul").toggle();
});

This was the simplest part. Now let's simulate other features of Select element. When you choose an option from the list (UL) it become selected option and is shown inside the link in DT. The function below replaces the HTML of currently selected item with the inner HTML of clicked link. At the end, it hides nested UL.

$(".dropdown dd ul li a").click(function() {
    var text = $(this).html();
    $(".dropdown dt a span").html(text);
    $(".dropdown dd ul").hide();
}); 

This looks more like Drop Down, but there are still some things that need to be done. First, once you reveal nested UL by clicking on Drop Down it remains visible. And that's a bug. Although there can be better solutions, I've came up with this one:

$(document).bind('click', function(e) {
    var $clicked = $(e.target);
    if (! $clicked.parents().hasClass("dropdown"))
        $(".dropdown dd ul").hide();
});

The function above checks each click on a page and if click occurred on some elements outside the dropdown it hides nested UL. Now this looks like regular Select element.

What about selected value?

Although it looks fine it will surely become a headache for developers. Select element has "value" attribute where you can store some data then needs to be sent to the server. This attribute is usually populated by ID's of records stored in the database. In our example with list of countries, it is more likely that some country ID will be needed for any serious processing on the server. So how to store these values and how to get selected one?

Let's modify HTML structure from the beginning of this tutorial and add <span> element inside links in UL. Each <span> have class "value" and real value inside it.

<dl class="dropdown">
    <dt><a href="#"><span>Please select the country</span></a></dt>
    <dd>
        <ul>
            <li><a href="#">Brazil<span class="value">BR</span></a></li>
            <li><a href="#">France<span class="value">FR</span></a></li>
            <li><a href="#">Germany<span class="value">DE</span></a></li>
            <li><a href="#">India<span class="value">IN</span></a></li>
            <li><a href="#">Japan<span class="value">JP</span></a></li>
            <li><a href="#">Serbia<span class="value">CS</span></a></li>
            <li><a href="#">United Kingdom<span class="value">UK</span></a></li>
            <li><a href="#">United States<span class="value">US</span></a></li>
        </ul>
    </dd>
</dl>

It would be strange to have codes next to country names in the list, so let's hide these spans.

.dropdown span.value { display:none;}

And that is the only change we'll need to make in order to make this Drop Down fully functional. Take one more look at click handler for links in UL – it replaces inner HTML of link in DT with inner HTML of clicked link. This means tha tlink in DT element will have <span> with value included.

var text = $(this).html();
$(".dropdown dt a span").html(text);

It's easy now to get selected value from our Drop Down. You can create a function like the one below to extract selected value. As the matter of fact, the very same function extracts selected value in the demo and shows this value under the Drop Down.

function getSelectedValue(id) {
    return $("#" + id).find("dt a span.value").html();
}

The code works in all major browsers: Firefox, Safari, Google Chrome, Opera(9.64), Internet Explorer 7 and 8. It works even in IE6, althoug it needs some polishing :)

Update (29.07.2009): Creating DropDown from SELECT 

Since many of you pointed out the issue with accessiblity (and I agree with that) I made another example that creates DropDown dynamically from SELECT element and binds click event to it. It still doesn't have up/down key navigation, though.

Instead of having hard-coded DL, we will have SELECT element with all the items here:

<select id="source">
    <option selected="selected" value="BR">Brasil</option>
    <option value="FR">France</option>
    <option value="DE">Germany</option>
    <option value="IN">India</option>
    <option value="JP">Japan</option>
    <option value="RS">Serbia</option>
    <option value="UK">United Kingdom</option>
    <option value="US">United States</option>
</select>

We will then use jQuery to dynamically create list from SELECT:

function createDropDown(){
    var source = $("#source");
    var selected = source.find("option[selected]");  // get selected <option>
    var options = $("option", source);  // get all <option> elements
    // create <dl> and <dt> with selected value inside it
    $("body").append('<dl id="target" class="dropdown"></dl>')
    $("#target").append('<dt><a href="#">' + selected.text() + 
        '<span class="value">' + selected.val() + 
        '</span></a></dt>')
    $("#target").append('<dd><ul></ul></dd>')
    // iterate through all the <option> elements and create UL
    options.each(function(){
        $("#target dd ul").append('<li><a href="#">' + 
            $(this).text() + '<span class="value">' + 
            $(this).val() + '</span></a></li>');
    });
}

Let me briefly explain the code here (you can check the entire source code in the second demo). It first reads all the <option> elements and selected <option> element as well. Next, it creates DL and DT with selected item inside it. After DL is being created, the code iterates throug <option> collection and create UL with list items inside it.

In order to fully bind this DropDown to SELECT element we need to refresh SELECT each time new selection is made in DropDown.
We have to modify click event handler for links in UL. It will get
the selected item from the DL and assign a value from <span> to
SELECT element.

$(".dropdown dd ul li a").click(function() {
    var text = $(this).html();
    $(".dropdown dt a").html(text);
    $(".dropdown dd ul").hide();
    var source = $("#source");
    source.val($(this).find("span.value").html())
});

That should fix the issue. Thanks everyone for pointing out this.

View SECOND demo

Wasn't that simple?

I think that reinvention of Drop Down wasn't that hard, although it might seems as is it. There are several features that need to be implemented in order to have a credible simulation, but each one of those is simple and straightforward. Have you ever had a need for reinventing Drop Down? What are your experiences? In any case, let me know your thoughts!

Let's discuss this on twitter.

81 Comments

  • Muhammad Mosa (July 28, 2009)

    Very very cool. I’ve seen that before, but never read a tutorial about how to make it. Great post Janko. And very helpful + inspiring.

  • jQueryGlobe (July 28, 2009)

    Interesting. It would be better to have tab focus and key navigation

  • tommy (July 28, 2009)

    Hello Janko,

    thanks for this great article. I really really appreciate your work on this issue.

    tommy

  • Max (July 28, 2009)

    Very nice tutorial !!

    Is it better with dropdown dd ul { display:block } by default ?

  • Patternhead (July 28, 2009)

    Great tut. I like the idea but it does have some accessibility issues.

    In an ideal world, it would have the same functionality as a standard drop down so you’d be able to access it using the regular keyboard shortcuts. Also, it would be nice if you could just use a regular dropdown and then replace it with your funky version if the user is running javascript.

  • Marc (July 28, 2009)

    Nice tutorial. you should implement it on your blog comments system ;-)

  • Janko (July 28, 2009)

    Thanks for the comments :)

    jQueryGlobe, Patternhead: Yes, this has its price, so regular dropdown "behind the scenes" can be a solution. What I also missed to mention is :focus class that should be included.

    Max: Dropdowns are in "collapsed" state by default, I doubt it will make sense to have it revealed by default.

    Marc: I will think about it in next version, for sure :)

  • sosergio (July 28, 2009)

    very cool. I’ve been several times in the need of reinventing the dropdown.
    I will try to build an asp.net user control from this.

    Thank you.

  • Brian (July 28, 2009)

    These are the little things that I try to improve in my designs. I never even thought about putting images and other elements in my drop-downs. Cool.

  • web design singapore (July 28, 2009)

    Wow another cool drop down menu! Thanks for the detail tutorial! Wonder what is the min version of browser support?

  • Brettgil (July 28, 2009)

    Nice job. I think with a few minor CSS adjustments you could fix the IE6 issues as well.
    Seems to function but the layout of the visual drop down is off.
    Thanks for posting the tutorials – keep them coming.

  • Sergejus (July 28, 2009)

    I woud definitely add sliding effect for this :)

  • Jeff L (July 28, 2009)

    You really need some :focus styles on there to help make this keyboard accessible.

  • HeySatan (July 28, 2009)

    Great looking manipulation.

    I tried doing this a while back and the lack of keyboard integration was the killer for me, it didn’t fly with an users I had test it.

    Here’s a link I found a while back that manipulates an actual select box element and turns it into a keyboard navigable, accessible version of what you did. It’s not as attractive but maybe combine the idea be and styles of you .

    http://www.brainfault.com/2007/07/23/select-box-replacement/

  • HeySatan (July 28, 2009)

    Never finished my last sentence… It’s not as attractive but maybe someone combine the idea behind braindefault’s and styles of the tutorial on this page.

  • Chris (July 28, 2009)

    Echoing Patternhead, while it’s a great concept, this would be an absolutely fantastic solution if jQuery built the alternate DL markup automatically using a real < select > tag as the source of data.

  • DrWatson (July 28, 2009)

    Maybe I am missing something but what if you are posting a form to the server and can’t use javascript for the post? Then since you are not using a real select element the selected data does not get posted. Wouldn’t it be better to create a hidden input with the selected value after clicking?

  • Lineker (July 28, 2009)

    Hey Janko, good job!

    I was wondering how would you integrate this with the code behind, so we can manipulate the value on C# or VB.NET.

  • Janko (July 28, 2009)

    Brettgil: yes, it doesn’t take much work for IE6 :)

    Sergejus: Good idea, some fast sliding would make it more attractive.

    Jeff L: Yep, I realized that after posting the tutorial :)

    HeySatan, Chris: This would resolve most of the issues. HeySatan, Thanks for the link!

    DrWatson: A hidden input is fine If you don’t use javascript/ajax.

    Lineker: HiddenField, DropDownList or runat="server" on Drop Down will make it accessible from the server.

  • Justin Beasley (July 28, 2009)

    I’ve been using a modified version of jQuery Stylish Select from Scott Darby:
    http://www.scottdarby.com/2009/05/jquery-plugin-stylish-select-unobstrusive-select-box-replacement/

    Demo:
    http://www.scottdarby.com/plugins/stylish-select/0.3/

    One thing I like about this is that it hides the browser select boxes (so in non or limited-JS browsers like phones, the original select still appears), and still supports [b]keyboard navigation[/b] and [b]proper usability[/b].

  • tommy (July 29, 2009)

    Wow, nice comments. I didn’t realized some things at first sight.

    I think keyboard navigation/shortcuts will be very helpful regarding accessibility issues.

    Nevertheless: a great job for modern browsers (I think you can skip IE6 support – like YouTube ;)) and backend functionality (almost, you can also skip accessibility there).

    I think this article http://www.456bereastreet.com/archive/200705/why_styling_form_controls_with_css_is_problematic/ is a good starting point if someone wants to know some things about changing the style (and maybe the behavior) of common and well known control elements.

  • Janko (July 29, 2009)

    Justion: The plugin seems to be good one, although it needs modifications for styling.

    Tommy: Sure, styling standard elements can really be a pain, that why you might consider "reinventing".

  • balii (July 29, 2009)

    Hi. I think the most impressive detail in this is that the whole code is only some lines and it works as a charm.

    You should keep this as a light, easy-to-understand version.

  • Mohammad Ashour (July 29, 2009)

    Cool innovative idea. My issue with it is accessibility, and others have hinted at this. The problem is non-javascript enabled clients can’t use this. (Nor would the hidden field work without JS to assign the value from the drop down). One solution that has been discussed is building the foundation using a select, then using jQuery to replace it with the newfangled drop down. Other than that I do love the innovative spirit behind this drop down, especially with the limited user interface options we have as web developers.

  • Valamas (July 29, 2009)

    The box feels great.

    As a few have stated, keyboard navigation would be a major plus. I.e., being able to TAB to the control and use the UP/DOWN/PageUp/PageDown controls. Not sure if this is possible.

  • Janko (July 29, 2009)

    Mohammad: Yes, that is an issue, and it should be done using <select> if required. For many line-of-business applications that rely heavily on javascript/ajax this wouldn’t be a big problem.

    Valamas: Actually you can tab to the control but you won’t be able to use up/down keys.

  • violinista (July 29, 2009)

    I would suggest a small improvement: You even don’t need span element; you can easily add value into parent LI element, like:
    [quote]
    <LI value=’RS’>Serbia</LI>
    [/quote]

    … which can easily be reached via jQuery’s .attr(‘value’).

    My 2 cents ;)

  • Bradvin (July 29, 2009)

    hey – cool implementation – one problem – in your source download, there are no files related to the demo. It seems you have zipped up the wrong folder :)

  • Andrew (July 29, 2009)

    Does not work in Firefox 3.5

  • Janko (July 29, 2009)

    violonista: Good point! The only reason why I added span is that I can easily replace the HTML of DT link without accessing any other elements.

    Bradvin: Ooops, my mistake. You can try now, I’ve fixed it.

    Andrew: Strange, what bug occurs in FF3.5?

  • Gry Dla Dzieci (July 29, 2009)

    I’m not the biggest fan of dropdowns. They break way too often and you’re left annoyed with the website that doesn’t work right.

  • battisti (July 29, 2009)

    last week i’m developing a web app that required a selec with icons, and i make myself script and today i foud it :(, but your idea was very good, if iimplements kyboard navigator it’s wil lbetter!

  • FULANO (July 29, 2009)

    Very cool, but when navegate by keyboard, should highlight the selection.

  • Janko (July 29, 2009)

    FULANO: It highlights the selection in demo if you use FF (:focus class)

  • Tim Wright (July 29, 2009)

    Nice technique, maybe it will revive the dropdown menu. I haven’t been seeing them lately

  • psd to html (July 29, 2009)

    Cool! i have just found it! but have you any ideas how to replace SELECT box tag with your script?

    Thanks

  • Janko (July 29, 2009)

    I updated the tutorial with the code that creates DropDown from <select>. Demo is also updated.

  • DevNet (July 29, 2009)

    Nice mate ,im going to try and edit it and give a fade in effect

  • Ethan (July 29, 2009)

    Great tutorial! It looks a lot like the one at NETTUTS.

  • Mark Aarhus (July 30, 2009)

    For adding keyEvents you could do something like this:
    In my example it requires the select tag to be inside the dl tag.
    It can easily be rewritten to suite the above example.

    $( ".dropdown" ).keyup(
    function( e ) {
    // get keyCode (window.event is for IE)
    var sKey = e.keyCode || window.event.keyCode;
    var $select = $(this).find( "select" );

    if(sKey == 40 || sKey == 38){
    if( sKey == 38 ){ // keyUp
    if( $select[0].selectedIndex == 0 || $select[0].selectedIndex == -1 )
    $select[0].selectedIndex = 0;
    else {
    $select[0].selectedIndex–;
    }
    } else { // keyDown
    if( $select[0].selectedIndex == $(this).find( "select option" ).length -1 )
    $select[0].selectedIndex = 0;
    else {
    $select[0].selectedIndex++;
    }
    }
    $(this).find( ‘a:first’ ).html( $(this).find(":selected").text() );
    return true;
    } else {
    return false;
    }
    }
    );

  • FULANO (July 30, 2009)

    I saw that it highlights on focus, but what i really thought was highlight the options when it get focus by the Keyboard. Get it? Trigger de same effect of hover.

    When we navigate to the selectbox and then press Enter, the options are showed, but when we try to select the option using the arrows it doesn’t highlight.

  • Janko (July 30, 2009)

    Mark: Thanks for the tip!!

    Fulano: If you mean to expand the options on focus I think that would be the strange behavior. I haven’t implemented up/down key navigation, but Mark’s code can resolve this issue.

  • Soh Tanaka (July 30, 2009)

    I like your approach Janko :-) Great job~

  • Marin (July 30, 2009)

    I don’t know why but on MacOSX FF3.0.11, I cannot see the comments (display:none on the div#commentlist)

    Nothing occurs when I submit the comment form… known bug for BlogEngine.NET?

  • psd to html (July 30, 2009)

    so, i could make SELECT BOX as display:none; and it will work. right?

  • Janko (July 30, 2009)

    Soh Tanaka: Thanks!!

    Marin: I don’t know why is that happening, I will check that. Thanks for letting me know!

    psd to html: Yep, it should work. Try hiding/unhiding SELECT in the demo with firebug :)

  • Bill (July 30, 2009)

    Awesome job!

    One challenge I’m having is that if it is below the scroll (or farther down on the page), clicking it makes it move to the top. If you take the sample and put a lot of <p> spaces so it’s farther down on the page, you will see when you select it you return to home (@the top).

    So drop downs toward the bottom are a little challenging.

  • panchicore (July 31, 2009)

    Es genial, very cool reinventing input fields.

  • Codie (July 31, 2009)

    Hey, this is pretty cool, thanks for the tutorial.

  • Mark Aarhus (July 31, 2009)

    I have developed the code for keyup and down some more. If the drop down is not open it just changes values/text on up/down key. If dropdown is open i navigates up and down the list.

    The code is from the dropdown styling I have developed for my own project, for my company.

    var oSelect = {
    mCurrent : -1,
    mTotal : 0
    }
    //==== Treating keys pressed
    $( ".dropdown" ).keyup(
    function( e ) {
    // get keyCode (window.event is for IE)
    var sKey = e.keyCode || window.event.keyCode;
    var $select = $(this).find( "select" );
    oSelect.mTotal = $(this).find( "select option" ).length;

    if(sKey == 40 || sKey == 38){
    if( sKey == 38 ){ // keyUp
    if( oSelect.mCurrent == 0 || oSelect.mCurrent == -1 )
    oSelect.mCurrent = oSelect.mTotal-1;
    else {
    oSelect.mCurrent–;
    }
    } else { // keyDown
    if( oSelect.mCurrent == oSelect.mTotal -1 )
    oSelect.mCurrent = 0;
    else {
    oSelect.mCurrent++;
    }
    }
    if( $( this ).find( "ul:hidden" ).length > 0 ) {
    $select[0].selectedIndex = oSelect.mCurrent;
    $(this).find( ‘a:first’ ).html( $(this).find(":selected").text() );
    } if( $( this ).find( "ul:visible" ).length > 0 ) {
    // loop through each result li applying the correct style
    $ul = $( this ).find("ul");
    $ul.children().each(
    function( i ) {
    if( i == oSelect.mCurrent ) {
    $select[0].selectedIndex = oSelect.mCurrent;
    $( this ).addClass("selected");
    } else
    $( this ).removeClass("selected");
    }
    );
    }
    return true;
    } else if( sKey == 13 ) {
    if( $(this).find( "ul li.selected" ).text() != "" ) {
    $(this).find( ‘a:first’ ).html( $(this).find( "ul li.selected" ).text() );
    return true;
    }
    return false;
    } else {
    return false;
    }
    }
    );

  • SJL Web Design (July 31, 2009)

    Hi Janko, thanks for the great tutorial, the drop down looks fantastic, thanks for sharing it with us.

  • Joseph Piché (July 31, 2009)

    This is so awesome. I hate select boxes, so this will totally redefine how I look at them.

  • Bill (August 1, 2009)

    Can somebody please see if this problem can be duplicated. It does it on IE7 (PC) and FireFox (Mac)

    On the Demo: http://www.jankoatwarpspeed.com/examples/reinventing-drop-down/

    1. Make the window size small so that you need to scroll down to access the drop down.

    2. Once Scrolled down click the drop down

    3. It will jump to the top of the page

    When I put this in the footer of a demo site it’s causing it to jump to the top of the page.

  • Janko (August 1, 2009)

    Ben: The easiest way (although there might be better ones) is to add "return:false;" to the end of handlers for click events.

    Mark: Thanks for sharing the code, once again :)

  • Webdesign Santhos (August 2, 2009)

    Looks pretty awesome although it would be great if you could also use the keyboard to navigate through the list. But very well written!

  • Romeo (August 2, 2009)

    Very nice is, that it works without js too :).

  • tom.higgy (August 3, 2009)

    Good work! I’ll be looking for something like this soon so very helpful.

    Accessible version is always better; start with something that works for everyone and enhance it with JS.

    Thanks!

  • TNk (August 3, 2009)

    excellent ty!! i was looking for a good solution to style <select>..maybe we can see some other styled form elements in the future? :)

  • Bill (August 4, 2009)

    Janko,

    I guess I’m not experienced enough to get the jump from happening, how would you apply: return:false;?

    Another example using jQuery can be found: http://www.filamentgroup.com/lab/jquery_ui_selectmenu_an_aria_accessible_plugin_for_styling_a_html_select/

    Thanks in advance for any help.

  • Janko (August 4, 2009)

    Thanks everyone!

    Bill: Check out this link on StackOverflow: http://stackoverflow.com/questions/265478/preventdefault-on-an-a-tag you can find explanation there for return:false, and also for another possibility and that is to use preventDefault() method.

  • dbone (August 4, 2009)

    Download link isn’t working for me.

  • Janko (August 4, 2009)

    dbone: I just tried and it works fine. Did you get any errors?

  • Bill (August 4, 2009)

    Janko,

    Thanks for the advice. I updated:

    $(".dropdown dt a").click(function() {
    $(".dropdown dd ul").toggle();
    });

    with

    $(".dropdown dt a").click(function(event) {
    event.preventDefault();
    $(".dropdown dd ul").toggle();

    and is seems to work fine now — no jumping.

  • Scott D (August 22, 2009)

    I wanted to utilize this on a page with multiple dropdowns and with the current config, it was only working with one per page. I re-wrote it a bit– and now it only affects the list that you’re clicking on. I’m no jQuery expert, so there’s a chance this is full of poor-use mistakes, but it works and I can have as many drop downs on a page as I want. Thanks for the idea, the styling, and the tutorial!

    The only other change I made is that each DL list needs to have a unique ID (obviously). So, "sample1", "sample2", etc would work fine.

    $(".dropdown dt a").click(function() {
    var dropID = $(this).closest("dl").attr("id");
    $("#"+dropID+" dd ul").slideToggle(200);
    });

    $(".dropdown dd ul li a").click(function() {
    var dropID = $(this).closest("dl").attr("id");
    var text = $(this).html();
    var selVal = $(this).find(".dropdown_value").html();
    alert("SELECTED: "+text+" | Value= "+selVal);
    $("#"+dropID+" dt a").html(text);
    $("#"+dropID+" dd ul").hide();
    // INSERT FUNCTION HERE getSelectedValue("partner_dropdown"));
    });

    $("dl[class!=dropdown]").click(function() {
    $(".dropdown dd ul").hide();
    });

  • Vivekanand (August 22, 2009)

    This is excellent, awesome enough – thanks a lot for your effort on this and sharing with us…

    Thanks,
    Vivek
    [http://www.developersnippets.com]

  • Vivekanand (August 22, 2009)

    Hi Again,

    This is not working for IE 6.0….

    Thanks,
    Vivek

  • Michael (August 24, 2009)

    Hello Janko,

    First off, excellent tutorial! After applying the code, I am having one slight issue though. We’re using multiple drop-downs and it seems that clicking on one opens both of them up. Is there any specific way to only open the one that is clicked on?

    We’ve applied unique ID’s to both of them, however, the issue still remains. Any help would be much appreciated!

  • Edward (August 27, 2009)

    Nice work. I like your tutorials and will definitely learn from them.

    Keep it up!

  • Moksha Solutions (August 31, 2009)

    nice tutorial. its really easy and simple

  • Matt (September 14, 2009)

    I needed this working for multiple dropdowns on a page, and I also wanted some nice effects with it. I also needed to accomplish hiding the drop down when somewhere else on the page was clicked or when an item was clicked….

    This is how I did it.

    $(".dropdown dt a")
    .click(function(){
    $(this).closest(".dropdown").find("ul").animate({opacity: ‘show’, height: ‘show’}, ‘fast’);
    return false;
    })
    .blur(function() {
    $(this).closest(".dropdown").find("ul").animate({opacity: ‘hide’, height: ‘hide’}, ‘fast’);
    });
    $(".dropdown dd ul a")
    .click(function() {
    // DO YOUR STUFF HERE ON CLICK
    return false;
    });

  • neel (September 17, 2009)

    Nice Articles. it can be enhanced using jquery toggle so it will look much better.

  • Vladimir (October 22, 2009)

    Hi Janco. Great stuff.
    Can you add in your second example guys’ code for multiple dropdowns and add to <span>s another class based on corresponded option value (then we can create icons designing <span>s)?

  • Marcus Downing (October 30, 2009)

    I was hoping to use the version which automatically converts <select> boxes into this. It works fine except for one thing – I can’t put rich code like images inside the <option> tags:

    <option><img /> abc</option> becomes <option> abc</option>

    Even using .innerHTML removes the rich formatting. Even putting the contents of the <option> in a CDATA doesn’t work:

    <option><![CDATA[<img /> abc]]></option> becomes <option></option>

    Is there any way round this? Sure I could write the transformed structure by hand, but that feels ugly.

  • Marcus Downing (October 30, 2009)

    I worked around my problem by putting an alternative copy of the contents into the label attribute, and pulling those out on conversion:

    <option label="<img /> abc">abc</option>

  • Vladimir (November 4, 2009)

    Sorry guys for my dilettantism

    But can anybody tell me what is my mistakes? I just wanna create few drop downs on one page – immediately after selects.dropdown_value, and use spans.value to create unique icons for each list item.

    $(document).ready(function() {
    createDropDown();

    $(".dropdown dt a").click(function(event) {
    event.preventDefault();
    var dropID = $(this).closest("dl").attr("id");
    $("#" + dropID).find("ul").toggle();
    });

    $(document).bind(‘click’, function(e) {
    var $clicked = $(e.target);
    if (! $clicked.parents().hasClass("dropdown"))
    $(".dropdown dd ul").hide();
    });

    $(".dropdown dd ul a").click(function() {
    var dropID = $(this).closest("dl").attr("id");
    var text = $(this).html();
    var source = $(this).find(".dropdown");
    $("#" + dropID + " dt a").html(text);
    $("#" + dropID + " dd ul").hide();
    source.val($(this).find("span.value").html())
    });
    });

    function createDropDown() {
    var selects = $(".dropdown_value");
    var idCounter = 1;
    var dropID = "dropdown_" + idCounter;
    jQuery.each(selects, function() {
    var source = $(this);
    var selected = source.find("option[selected]");
    var options = $("option", source);
    source.after(‘<dl id="’ + dropID + ‘" class="dropdown"></dl>’);
    $("#" + dropID).append(‘<dt><a href="#">’ + selected.text() +
    ‘<span class="value">’ + selected.val() + ‘</span></a></dt>’);
    $("#" + dropID).append(‘<dd><ul></ul></dd>’);
    options.each(function() {
    $("#" + dropID + " dd ul").append(‘<li><a href="#">’ + $(this).text() +
    ‘<span class="value">’ + $(this).val() + ‘</span></a></li>’);
    });
    });
    idCounter++;
    }

  • Colum (November 7, 2009)

    i like mootools more, but still a great job!

  • Vladimir (November 13, 2009)

    I’ve change code for multiple drop downs with spans which can be used as icons by myself.

    $(document).ready(function() {
    createDropDown();

    $(".dropdown dt a").click(function(event) {
    event.preventDefault();
    var dropID = $(this).closest("dl").attr("id");
    $("#" + dropID).find("ul").toggle();
    });

    $(document).bind(‘click’, function(e) {
    var $clicked = $(e.target);
    if (! $clicked.parents().hasClass("dropdown"))
    $(".dropdown dd ul").hide();
    });

    $(".dropdown dd ul a").click(function() {
    var dl = $(this).closest("dl");
    var dropID = dl.attr("id");
    var text = $(this).html();
    var source = dl.prev();
    $("#" + dropID + " dt a").html(text);
    $("#" + dropID + " dd ul").hide();
    source.val($(this).find("span.value").html())
    });
    });

    function createDropDown() {
    var selects = $("select.dropdown_value");
    var idCounter = 1;
    selects.each(function() {
    var dropID = "dropdown_" + idCounter;
    var source = $(this);
    var selected = source.find("option[selected]");
    var options = $("option", source);
    source.after(‘<dl id="’ + dropID + ‘" class="dropdown"></dl>’);
    $("#" + dropID).append(‘<dt><a href="#">’ + selected.text() + ‘<span class="value">’ + selected.val() + ‘</span></a></dt>’);
    $("#" + dropID).append(‘<dd><ul></ul></dd>’);
    options.each(function() {
    $("#" + dropID + " dd ul").append(‘<li><a href="#">’ + $(this).text() + ‘<span class="value">’ + $(this).val() + ‘</span></a></li>’);
    });
    idCounter++;
    });
    }

  • Mr F (December 11, 2009)

    Very nice. I’ve been planing on doing something like this for a while! [b]Thank you![/b]

  • mattywix (December 23, 2009)

    Anyone care to turn this into a JSF (javaserverfaces) component?

  • mattywix (December 23, 2009)

    heres how to integrate this with icefaces:
    <script>
    var $j = jQuery.noConflict();

    as per:
    http://docs.jquery.com/Using_jQuery_with_Other_Libraries#Including_jQuery_before_Other_Libraries

  • Pankaj (December 26, 2009)

    Great tip..I was exactly looking for the same..you made my day..Thanks