Combo boxes in forms on Web pages

The phrase combo box is used here to denote a construct in a form, allowing the user to make a choice among predefined alternatives or type his own alternative. In HTML terms, this would mean a combination of a select element and an input type="text" element. In HTML at present, you can only include such elements as two separate, mutually independent constructs. You can however include JavaScript code which checks the consistency on submit or, better still, dynamically modifies the form so that the text input field is enabled if and only if the select menu choice is particular one (like "None of the above, please specify:"). In some situations the field could be invisible when it is disabled.

Quite often nowadays, "combo box" seems to mean just a dropdown menu (pulldown menu) created by a select element. But it seems that the original meaning is "combination box" as outlined above, and that's the meaning used here. The need for such constructs arises naturally when you set up a form where a question should normally be answered by selecting one of given alternatives but it is possible that none applies, in which case you'd like to ask the user to type his choice in his own words.

There is also a different idea of combining free text input and menu selection: using a text field so that it can be filled either by typing directly or by selecting an option from a menu which causes the text field to be filled. An approach similar to the one discussed here could be applied: include the text field normally, and if JavaScript is enabled, include dynamically a select menu so that when a selection is made, the text field is filled with text corresponding to the option selected. The main problem with that approach, as compared with the one discussed in this document, is that the user could not make a choice from a menu when JavaScript is disabled.

The simple basis: two independent fields

Due to the simplicity of the form concept in HTML, you cannot set up a genuine combo box in HTML. You can just set up two separate fields. This means that the server-side form handler should, among the data consistency checks it performs, verify that the data from the text input field is present and nonempty if and only if the choice from the select menu was "None of the above, please specify:" or whatever you decided to put there. This is something you should do in any case, as a fallback for situations where more advanced methods do not work. This does not mean writing separate pages for different browsing situations. Instead, you use some nice constructs within a single page so that the page "behaves" differently. And the simple fallback, consisting of two fields, should be done first, because it makes this part of the form fully functional, though not optimal, and it is needed anyway, and the enhancements will be built around it. It could be something like the following:

<form action="http://jkorpela.fi/cgi-bin/checkcombo.pl"><div>
<select name="menu">
<option value="0" selected>(please select:)</option>
<option value="1">one</option>
<option value="2">two</option>
<option value="3">three</option>
<option value="other">other, please specify:</option>
</select>
<input type="text" name="choicetext"></div>
<div><input type="submit" value="submit"></div>
</form>

This is how it looks like on your browser:

(If you try to submit it, you will get the form data echoed back, together with a note telling whether the data would be rejected or accepted by the server-side checks discussed below.)

The server-side form handler would then to check first that the value of the menu field is not 0 (indicating that the user made no choice). Then it would use the value of that field for further processing, unless the value was other, in which case the value of the choicetext field would be used, perhaps after some preprocessing. And it should check that menu has value other if and only if the value of choicetext is nonempty. It naturally depends on the application what should be done if one of the checks fails.

We will not go into these issues here. Instead, we assume that you have this robust, though simplistic, implementation done and tested, and consider the optional client-side enhancements. However, for readers with basic understanding of Perl, here's a simple piece of code for doing the checks in Perl (with the $in hash containing the form fields as received and with resulting return page being cumulated to variable $output):

  if($in{'menu'} eq 0) {
    $output .= "<p><strong>Rejected</strong>: no selection made.</p>\n"; }
  elsif($in{'menu'} eq 'other' &&
          (!defined($in{'choicetext'}) || length($in{'choicetext'}) ==  0)) {
    $output .= "<p><strong>Rejected</strong>: option \"other\"".
       "selected but text input box not filled.</p>\n"; }
  elsif($in{'menu'}!='other' &&
        defined($in{'choicetext'}) && length($in{'choicetext'})>0) {
    $output .= "<p><strong>Rejected</strong>: option \"other\"".
       "not selected but text input box filled.</p>\n"; }
  else {
    $output .= "<p>Accepted.</p>"; }

When a form submission is rejected due to such checks, the user can probably use the Back button (or its equivalent) to get back the form with his data, fix it, and submit anew. A more robust solution would work so that the page returned by the script does not only explain what's wrong but also contains a copy of the form, with user-supplied data prefilled and unacceptable data highlighted. Exactly how this should be done depends on the application, of course.

Adding client-side check on submit

A simple client-side enhancement is to perform on form submission checks which correspond to the server-side checks. The purpose is to give the user faster feedback. An error in filling a form is probably easier to fix when it is reported immediately. For our sample form, we could just add the attribute
onsubmit="return valid(this.menu,this.choicetext)"
into the form tag and include the following JavaScript code (via a script element):

function last_choice(selection) {
  return selection.selectedIndex==selection.length - 1; }
function valid(menu,txt) {
  if(menu.selectedIndex == 0) {
    alert('You must make a selection from the menu');
    return false;} 
  if(txt.value == '') {
    if(last_choice(menu)) {
      alert('You need to type your choice into the text box');
      return false; }
    else {
      return true; }}
  else {
    if(!last_choice(menu)) {
      alert('Incompatible selection');
      return false; }
    else {
      return true; }}}

This presumes that the "Something else" choice in the menu is the last one in the menu, as it usually is.

You can test it on your browser:

We could consider various enhancements to this approach, such as performing these checks also when the select menu choice or the text input field is changed or when the field after them is focused on. That would give even faster feedback to the user. But here we shall consider the approach which lets us, in some cases, prevent the user from making errors.

A dynamic combo box

Ideally, we would like to make a combo box appear so that initially there is just a select menu, and when a choice like "Other" is selected, a text input box appears (alongside with the menu, or instead of it) for user input.

We use "DHTML" techniques in an attempt to make the text input field both disabled and invisible unless the "Other" choice is selected. The relatively simple technique used for that here works on all reasonably modern versions of commonly used browsers, including IE from version 4 upwards. (To create something similar on very old versions of Netscape would require an extra effort.) We additionally use simpler JavaScript techniques which prevent the field from being focused on; this should work on all JavaScript-enabled browsers. (Compare to techniques for making a field readonly.) The code means the following additions to what we discussed above:

You can test this on your browser (the code is also available as a separate document):

This works best on modern JavaScript-enabled browsers; then the text input field is not visible at all unless "other, please specify:" is selected. In the currently rare case of a JavaScript-enabled browser that does not support the CSS property visibility, the text input field is there all the time but you cannot write into it, or even focus on it, unless "other, please specify:" is selected. The reason is that even if the visibility property won't work, the browser will still process the event of focusing on the text input field. That means executing the check_choice() function. And when JavaScript is unsupported or disabled, there is just a select menu and an input box, and the server-side script needs to take it from there.

A modern approach

Since practically all JavaScript-enabled browsers support the invisibility property, you could simplify the approach as follows:

Note: One might also consider using the display property for the text input field, setting it to none initially (in JavaScript) and changing it to inline when needed. However, changing the value of the display property generally requires reformatting (redraw) of the page, whereas visibility only affects the particular element.<(p>