Introduction

Because I come from a Windows programming background, I’ve come to miss a very useful user interface device that seems to be oddly absent from web and online development environments. It’s called the combo box and they’re used in almost every common Windows application. The place where you type the URL in the browser, the way you change the font in Microsoft Word, they’re part select, part text input and they’re great.

HTML Form Builder

Combo boxes provide user input without sacrificing pre-selected or pre-loaded options and I’m surprised the opportunity to use them are lost to web applications. Web developers have to choose between one or the other and for someone trying to create user-friendly forms, it’s frustrating to see such a useful weapon not in their arsenal.

Now, I have found a few attempts at implementing the combo box on the web, but these attempts, while creative, have some markup problems that just don’t do it for me AND some of them cost money. And by money, I mean over $100 for the implementation (I know). So the method I’ve come up is a clean, reusable solution that only asks for one additional line of markup and it’s going to cost you nothing. Let’s get started.

See it in Action

I have created a generic “Create Account” form that you might see anywhere on the web. Notice the fields “Referred By” and “Reminder” look like select boxes. Now try typing in them. Nice, eh?.

Implementation

  1. Download the files.

  2. Upload ‘comboBox.js’ and ‘placeHolder.html’

  3. Include the ‘comboBox.js’ on the page you want to implement it on.

    <script type="text/javascript" src="comboBox.js"></script>
    
  4. Add the class ‘comboBox’ to any select element you wish to convert into a combo box. Make sure the select element has a unique name attribute.

Proper implementation in your markup should look something like this:

<label for="ReferredBy">Referred By</label>
<select name="ReferredBy" class="comboBox">
    <option value=""></option>
    <option value="Friend">Friend</option>
    <option value="Magazine">Magazine</option>    <option value="TV">TV</option>
</select>

Things You Should Know

  • Tested and works on the big ones: IE6, Firefox 1.0.4 and Safari. Also Netscape 8. Opera has problems with alignment, but there is still hope. Works in Opera, too.

  • Layout: The select control and textbox have a fixed height. This does not scale well when users change their font size. Additionally, a change in font size does not trigger the ‘onresize’ function, so the textbox coordinates do not change appropriately. Kevin helped me out with the script and the inputs now resize automatically based on the select element’s width and height. If you resize the font text, you can refresh the page and see it fix itself. We did, however, notice that the positioning function doesn’t work if you use a container id that uses ‘position:relative’ to control your layout. We’re working on it.

  • Usability Issues: Users are not familiar with this type of control. I placed “Type here.” in the textbox, but is that enough? Will users understand what this control is capable of doing?

  • Programming Concerns: Browser sniffing is never fun or good practice, but it seems to be the only way to accomplish our goals here since form interfaces are incredibly different across browsers.

How It Works

Basically, we’re attacking this problem with a little CSS and DOM. The plan is to layer a textbox directly over a select control. First, we need to find the select elements that need to be converted. ‘setCombobox’ is a simple JavaScript loop that runs when the page is loaded and finds all select controls with the class name ‘comboBox’.

function setCombobox() {
    combos=document.getElementsByTagName('select');
    for(i=0; i<combos.length; i++) {
        sClassProperties = combos[i].className.split(" );
        if(sClassProperties[0] == comboBox) {
            // do something
        }
    }
}

Once we find the elements that need to be converted, we have to figure out exactly where on the page we need to place the input element. To find the x and y coordinates of our select elements, we’ll create two global variables that are initiated with a call to ‘findPos()’:

nTop = findPosY(combos[i]);
nLeft = findPosX(combos[i]);function findPosX(obj) {
    var curleft = 0;
    if (obj.offsetParent) {
        while (obj.offsetParent) {
            curleft += obj.offsetLeft;
            obj = obj.offsetParent;
        }
    }
    else if (obj.x) {
        curleft += obj.x;
    }
    return curleft;
}

The function above is courtesy of [the genius work](http://www.quirksmode.org/js/findpos.html Find your coordinates) done over at Quirksmode. ‘findPosX()’ and ‘findPosY()’ do the same thing, except X uses ‘offsetLeft’ and Y uses ‘offsetTop’ as reference points to determine their locations. Now that we have the coordinates, we can then create the input elements.

function initTextField(ctrl) {
    //create div that textfield will rest inside of
    textDiv = document.createElement("div");
    textDiv.style.position =" absolute";
    textDiv.style.top = nTop + px;
    textDiv.style.left = nLeft + px;
    textDiv.style.backgroundColor =" transparent";
    textDiv.style.zIndex =" 1";
    ctrl.parentNode.insertBefore(textDiv, ctrl.nextSibling);    //create textfield
    textfield = document.createElement("input");
    textfield.setAttribute(type, text);
    textfield.id =" txt" + ctrl.name
    textfield.style.border =" 1px solid #ccc";
    textfield.style.borderRight =" none";
    textfield.style.paddingLeft =" 3px";
    textfield.style.color =" #ccc";
    textfield.style.width =" 123px";
    textfield.style.height =" 18px";
    textfield.className =" comboText";
    textfield.value =" Type here ... ";
    textDiv.appendChild(textfield);
} 

At this point, things were working beautifully in Firefox, and horribly in IE. Thankfully, this [life saving hack](dotnetjunkies.com/weblog/jking/posts/488.aspx How to cover a control with an iframe) I came across explained why and gave me a solution. In IE, nothing can hover over a select control, even if it has a higher z-index. Well, an iframe can prop any div up above any control when placed directly underneath the control. So, now we need to detect which browser is being used, and add an iframe directly underneath the control for IE users.

function initIframe(ctrl) {
    hackFrame = document.createElement("iframe");
    hackFrame.setAttribute(src, placeHolder.html);
    hackFrame.setAttribute(scrolling, 0);
    hackFrame.setAttribute(tabindex, -1);
    hackFrame.style.top = nTop + px;
    hackFrame.style.left = nLeft + px;
    ctrl.parentNode.insertBefore(hackFrame, ctrl.nextSibling.nextSibling);}

A couple of things to note here. The frame has a source of ‘placeHolder.html’. This is a blank web page. It needs to be there, or else Service Pack 2 users will get a security warning. Also, the frame is being implemented via the DOM directly after the div containing the textbox. Once this mind numbing 40 hour hack finding marathon was over, the finish line came into view. The final step was to make the textbox and select control act as one.

combos[i].onchange=function() {
    txtCombo = document.getElementById("txt" + this.name)
    txtCombo.value = this.options[this.selectedIndex].value;    
}

If the user selects a new value in the select element, the text field is updated to reflect that value.

And that’s all you need. With minimal markup and a healthy dose of J-juice goodness, we can provide users select elements on steroids. As always, this is a work in progress and any help finding bugs and making improvements is always welcome.

Addendum

Going through such lengths to port a desktop control on to the web is definitely experimental. At the same time, I do feel it is possible to get this stable enough to use in a production environment. You must also consider the environment you are developing for. It may be a stretch to use something like this in a high traffic eCommerce site. But, if you are converting all of your company’s windows apps to web apps, and the users have become accustomed to this type of control, it may fit in perfectly. I have been in two situations where this control was either absolutely needed, or would have made the user experience better. I am sure plenty of people out there are in the same boat. There is not a lot of documentation on the subject, so hopefully this will help someone out.

HTML Form Builder
Ryan Campbell

Upgrade Your Select Element to a Combo Box by Ryan Campbell

This entry was posted 5 years ago and was filed under Features.
Comments are currently closed.

· 47 Comments! ·

  1. Ryan Campbell · 5 years ago

    Good catch. I definitely need to address the tabindex. The link you posted did not work. I would like to read alternate methods of reading classNames, so please post it again if you stop back. Our comments strip HTML, so you have to use the textile version.

    Thanks

  2. Matthew Pennell · 5 years ago

    Oops, sorry – might want to address that in your Live Preview then, as that shows HTML markup.

    The link is Snook’s list of getElementsByClassName functions with several different ways to match class names.

  3. graste · 5 years ago

    Well done. Have to read the whole article later this day, as I only clicked to the example. It works quite well for a beta. Anyhow, in Opera 8.01 it looks like this . It degrades somewhat gracefully. But perhaps it is only a small edit, that fixes the behaviour.

  4. Ryan Campbell · 5 years ago

    We messed around with layout all last night, and got it to work individually in each browser, but not all at the same time.

    Graste: In the screen you linked to, were you able to type into the text fields or were they disabled?

    Matt: Thanks for the link – I will be implementing that soon.

  5. luxuryluke · 5 years ago

    Yes, if you can get the tabbed feature working properly, this will be a nice little feature to add… but perhaps i’ll just add it myself now. Tabbing should properly select the dropdown menu, so that as it’s selected, i can down arrow properly to quick-select what i want by typing an alpha/numeric key and hitting return to select. ‘returning’ now just sends the form, an unexpected reaction. This could be the reason why your windows style combo box has never quickly been implemented, and is not a widespread practice. It is clever and well done… it’s just short of perfection though. nice work!

  6. Leszek Swirski · 5 years ago

    I did something like this recently, though I’d prefer it if no one used it yet, since it’s a WIP. I will release it once I’m done. Also, if anyone notices any bugs, then contacting me would be much appreciated.

    http://leszek.swirski.co.uk/nt/searchcomps.aspx

  7. Ryan Campbell · 5 years ago

    Very impressive Leszek.

  8. Leszek Swirski · 5 years ago

    All the more so being that it is, apart from the slider on the same page, the first piece of Javascript that I’d ever written that didn’t do something banale like showing and hiding a div.

  9. Craig · 5 years ago

    Very Nice! Is there a way that you can still use the selected attribute of option. For example

    Referred By

    Friend
    Magazine
    TV
    
  10. Ryan Campbell · 5 years ago

    It is possible, but requires tweaking of the code. I had to write a form recently that users can save and update, which meant my combo boxes selected values needed to be correct, etc. It took a handful more lines, but wasn’t too bad. I don’t have the code on me though :(

  11. pixelEngine · 5 years ago

    Value isn’t getting passed properly for me? I can get the 3 option values to pass if i select one of them…. but if I type into the field and then progress to the next page I lose any value at all in that field?

    Any ideas?

    Cheers.

  12. Ryan Campbell · 5 years ago

    You can access the value from the input field by adding “txt” to the id of the select element. So, a select element named “Referred” would be accessed like this:

    Request(“txtReferred”)

  13. hghg · 5 years ago

    Everyone needs a hug.

  14. Gustav · 5 years ago

    Awesome. Any thought of autocompletion added to the textbox? It’s still the one thing that Desktop apps have better…will it be difficult to implemen? (not offering =] I’m primarily into ruby and know only rudimentary JavaScript) If it could autocomplete the displayed text between the and tags, but send the value through… Thanks, and great job!

  15. James · 5 years ago

    Is it just me, or are the examples totally borked in FF 1.5?

  16. Eric · 5 years ago

    The combobox works perfectly on my Ffx 1.5.0.1. However, I am trying to use the selected attribute as well. I would like to initialize the selected value…

    anyways, this combobox solution is the best i’ve found and thanks so much!!!!

  17. Eric · 5 years ago

    Hi,

    Just found out how to use the selected attribute:

    inside inittextfield(ctrl) function, change

    initextvalue = ctrl.options[ctrl.selectedIndex].value; if (initextvalue == “”) { textfield.value = “Type Here”; textfield.style.color = “#ccc”; } else { textfield.value = ctrl.options[ctrl.selectedIndex].value; // initially “Type Here” }

  18. FF.redo · 5 years ago

    Everyone needs a hug.

    Yes, buddy, you’re right.

    This is an excellent script. Add a personal value in combo box is very usefull.

    Thanks again

  19. Piyush · 5 years ago

    this is great. just what i’ve been looking for. now all I need to do is to perform a search dynamically so that the user only sees items beginning with the input text

  20. James · 5 years ago

    I have the same problem as pixelEngine (http://particletree.com/features/upgrade-your-select-element-to-a-combo-box/#4158)

    If I select an option value it works. If I type in the field, the value does not get sent with my form. I’ve tried with IE, FireFox, and Konqueror. I don’t understand your response (http://particletree.com/features/upgrade-your-select-element-to-a-combo-box/#4159)

    Can you expound on how to make it send a typed value with the form?

  21. Justin · 5 years ago

    The input text box positioning wasn’t working for me because I have a container div with auto margins so I used the Prototype function “Position.positionedOffset” to replace findPosY and findPosX it seems to be working now.

  22. Esteban Rodriguez · 4 years ago

    Good work, nice thinking. I’ve been evaluating a couple of solutions for an html combo box implementation and this is the cleverest I’ve found yet,

    cheers,

  23. Mike · 4 years ago

    Very nice script!

    I’ve been playing around with it for a bit today, but can’t seem to figure something out. I’m trying to make it so you can only write in the first option (which would be blank). Because if I allow people to edit what’s in the dropdown originally. I’d end up having a lot of weird stuf in the database.

    Any way you could help me out?

    Thanks!

  24. Mike · 4 years ago

    Everyone needs a hug.

  25. Chris Maunder · 4 years ago

    Fix for getting the value from the text box:

    1. Make sure you have an empty entry as the first option in your SELECT box

    2. Add this to the end of function inittextfield(ctrl):

    function inittextfield(ctrl) { selectWidth = ctrl.offsetWidth;

    ...textfield.onchange=function() {
        ctrl.options[0].value = document.getElementById("txt" + ctrl.name).value;
    }
    

    }

    As text is typed into the text box the value of the empty option will be set to the text and then you simply need to call Request[“ComboElement”] to get the value.

  26. Xaprb · 4 years ago

    This is nicely done. I created a similar combo box before I found yours. And I had the same gripe about people charging money (WTF?!?!?!?!). Mine is at http://www.xaprb.com/blog/2005/09/29/javascript-combo-box/.

  27. Everyone · 4 years ago

    Everyone needs a hug.

  28. David Levin · 4 years ago

    Nice work. I have been completely frustrated with the standard SELECT box. Hopefully more web sites will implement this!

  29. Mark · 4 years ago

    Nice work also Ryan. My version only supports IE, but that is all we are allowed to use. I also fixed some rough spots and am now completely satisfied with the results.

  30. johan duflost · 4 years ago

    Very interesting implementation.

    Here are the problems I had:

    • multiple window.onload functions (conflict with existing onload functions)

    • window.onresize is called in internet explorer when the page loads and contains scrollbars !!!

    • ‘Type here’ text field value not necessary when you need to display a preselected value.

    • text field ‘name’ attribute missing

  31. Xaprb · 4 years ago

    Something you might note: the LABEL element’s ‘for’ attribute ought to match the id of a form element — not the name. In your example, the SELECT element doesn’t have an id attribute at all.

    Reference: http://www.w3.org/TR/html4/interact/forms.html#adef-for

    I repeat, well done :-)

  32. Clark · 4 years ago

    Very interesting work. I am trying to implement it on an intranet app, where previously the entries were ONLY text entry. We want to give the option to pick from a list OR type in an entry. It works nicely, but I am having problems passing the value of anything typed. I can retrieve the value from the list just fine. Any more wisdom on making the controls act as one?

  33. Clark · 4 years ago

    Update:

    I didn’t see the post above about adding this code

    textfield.onchange=function(){ ctrl.options[0].value=document.getElementById(“txt”+ctrl.name).value; }

    to the inittextfield(ctrl) function

    Now on the page receiving the form data I can Request.Form(“control_name”) and I get whatever value is in the text box.

  34. Clark · 4 years ago

    I needs a hug.

    I have been looking at this for a while. I have it working and it is nice, but end users being end users, they have dug up one thing that I can’t find a solution to. If you select something from the list, the value is passed properly. If you type something in the FIRST (blank) box, that value is also passed properly, but if you type over one of the drop down items, the original value is passed. I either need to find a way to disable typing for all but the first, or find a way to make it pass whatever value is entered in any slot.

  35. Clark · 4 years ago

    Alright, this will be my last post, I just wanted to follow up. The answer to allowing users to input only on the first (blank) line is this:

    In the comboBox.js file, in the inittextfield(ctrl) function, add this code:

    textfield.onclick=function(){ click(); }

    And then on the page where your select box is located, place this code:

    function click(){ if (document.form.comboBox.selectedIndex == 0){ //whatever path to your combo box } else{ window.alert(‘You cannot overwrite an existing Entry\r\nEnter a new Entry in the blank line at the\r\ntop of the list if you don't see your Data listed.’); document.form.comboBox.focus(); } }

    I realize this is only good if you have just one combo box on your page, it gets more complicated as you try to account for multiples.

    Ryan, thanks so much for this very useful code!

  36. xczxczx · 4 years ago

    Everyone needs a hug.

  37. xxczx · 4 years ago

    Everyone needs a hug.

  38. Kevin Hansen · 4 years ago

    ryan, i have been trying to get the selected attribute of an working with no avail. do you have the code somewhere? great little control nice work!

    any help would be greatly appreciated.

    thanks!

  39. Kevin Hansen · 4 years ago

    yikes, part of my comment had html in it.

    correct comment: selected attribute of an option working.

    sorry for the confusion.

    thanks!

  40. rob cubert · 4 years ago

    Thank you for the webpage http://particletree.com/features/upgrade-your-select-element-to-a-combo-box/ It greatly helped me and i appreciate what you have done.

  41. Arnima · 4 years ago

    hi I need a help regarding the combox box.I am creating a web page in which i have two combo boxes.How can i chnage the content of one combo box by selecting an element in other combo box. example: suppose the first combo box contains the list of countries and the second combo box contains the list of cities. now when i select a country in first combo box the other should list the cities in that country. How can this dynamic changing be done?? I mean what code should i write???

    Please send me a mail with the exact code for implementation. my mail id : arnimavidyarthy@gmail.com

    Thanks Arnima Vidyarthy

  42. Charles p. · 4 years ago

    I think I am having the same issue as Kevin Hansen. I am trying to set the default value of the text field to be the selected item in the option list.

    This works fine in IE6 but Firefox 2.0 doesn’t like it at all. It seems that Firefox is not setting the selectedIndex of the control until after this code fires (?) so that if an option has the selected attribute IE sets it as the selectedIndex property of the select control before this script fires but Firefox waits till afterward for some stupid reason.

    Any clues?

  43. Cliff Record · 4 years ago

    Do you have an example that works in xslt?

  44. Andy Earnshaw · 4 years ago

    Just a few quick notes. I came across this page whilst trying to improve my own combo box design I’m using in one of my Windows Sidebar gadgets. You should add an onlick that sets focus back to the text input when a selection is made. Secondly, add an onkeydown to the text input for when up & down is pressed. ie:onkeydown=”if (event.keycode == 38 || event.keycode == 40) selectBoxId.focus(); window.setTimeout(textInput.focus, 10);”Obviously replace selectBoxId with your own select element’s id. As the event fires before it bubbles, the keypress is actually sent to the select box, an then sets the focus back to the input. What i’m trying to do with mine is purely aesthetic - when hovering over the input box I want the select box to light up. Unfortunately, I don’t think you can trick any browser into thinking the mouse is somewhere it’s not :)Andy

  45. Nibu · 3 years ago

    Everyone needs a hug.

  46. Nibu · 3 years ago

    I cannot Down load the code files

  47. Mary · 3 years ago

    Everyone needs a hug.