Random Thoughts of a Scatterbrain.

Javascript Combobox

Want to leave a question, comment, or some criticism? Click here!

1. Motivation

The default set of HTML controls do not include functionality like that of the WinForms combobox. For UI designers, this is usually resolved by having both a text box and a dropdown select box and allowing users to use one or the other. However, the big drawback of this approach is that it takes up what could be valuable real estate space and it's not necessarily the most elegant solution to the problem.

2. Objective

Using cross browser compatible DHTML, create a WinForms style combobox which allows users to enter values into a textbox or select values from a dropdown linked to the textbox. There are various possible solutions as to how to render the dropdown (including using a control replacement technique); I've chosen to use a floating unordered list (UL) simply because the structure for the UL is simpler in structure (less keystrokes!) and it allows for much more customization than a typical select, including the ability to create nested options!

3. Auxiliary Files/Prerequisites

To try this out for yourself, you will need the following:

  • A modern browser. Either Internet Explorer 5.5+ (IE) or any newer version of Firefox (FF). I've tested with IE 6 and FF 1.5. I'm not sure that it'll work in Opera.
  • My custom debugging console. I initially wrote this for my jsSolitaire game. Since then, I've made some slight tweaks and improvements to it. Some of it needs to be cleaned up, but otherwise, it's a very handy replacement for window.alert().

4. What is It?

This is a custom WinForms style "combobox" which allows for entry of data by either typing in a value or selecting a value from a dropdown list. This is a very quick-and-dirty implementation done as a proof of concept for a client. Feel free to take some hints from this and build something better as this code is very specific.

The combobox, as implemented, has the following features:

  • Auto-complete. As you type, the script runs through a binary search through the "options" (actually list elements) to find the best match value. This search can be improved by adding support for:
    • Selecting the lowest value that matches the criteria. For example, when you type "p", it matches "pineapple" and not "papaya" since "pineapple" is the first match using a pure binary search algorithm.
    • Using a property to hold the last known match. There's no reason to do a binary search for "ma" and then "man" from scratch. The search should keep tabs on where the last match was found when a user is typing.
    • Note that for the binary search to work, the list item values must be in sorted ascending order.
  • Direction pad nagivation. You can navigate the dropdown list by pressing up or down on your directional pad. Hit enter to select a value.
  • Value highlighting. Highlighting on both mouseover and directional pad navigation to show current selection.
  • Auto-hide. The dropdown list fades out after a few seconds of inactivity.

5. Styling the List

Rather than covering the markup and script line-for-line, I've decided to just cover the most important part of the script, the styled, unordered list.

The advantages that this approach offers, as opposed to using a select dropdown, are:

  • It's possible to create true, multi-level selects (as opposed to using indented text with select and option elements.
  • No bleed-through that you get with select elements and no need for special code for handling it.
  • Better visual customization of the dropdown list.

We start with a typical unordered list. Since I love fruits (especially tropical fruits), we'll use a list of fruits.

Typical unordered list
  • Apple
  • Apricot
  • Avocado
  • Banana
  • Blueberry
  • Cactus Pear
  • Cantaloupe
  • Cherry
  • Dragon Fruit
  • Fig
  • Grape
  • Guava
  • Honeydew
  • Kiwi
  • Lemon
  • Mango
  • Nectarine
  • Orange
  • Papaya
  • Passion Fruit
  • Peach
  • Pear
  • Persimmon
  • Pineapple
  • Plum
  • Pomegranate
  • Raspberry
  • Star Fruit
  • Strawberry
  • Tangerine
  • Watermelon
Typical unordered list (HTML)
<ul class="value_list">
    <li>Apple</li>
    <li>Apricot</li>
    <li>Avocado</li>
    <li>Banana</li>
    <li>Blueberry</li>
    <li>Cactus Pear</li>
    <li>Cantaloupe</li>
    <li>Cherry</li>
    <li>Dragon Fruit</li>
    <li>Fig</li>
    <li>Grape</li>
    <li>Guava</li>
    <li>Honeydew</li>
    <li>Kiwi</li>
    <li>Lemon</li>
    <li>Mango</li>
    <li>Nectarine</li>
    <li>Orange</li>
    <li>Papaya</li>
    <li>Passion Fruit</li>
    <li>Peach</li>
    <li>Pear</li>
    <li>Persimmon</li>
    <li>Pineapple</li>
    <li>Plum</li>
    <li>Pomegranate</li>
    <li>Raspberry</li>
    <li>Star Fruit</li>
    <li>Strawberry</li>
    <li>Tangerine</li>
    <li>Watermelon</li>
</ul>

Notice that we've added the value_list CSS classname to the list. To make this work like a dropdown box, we'll obviously need to first clip the visible area of the UL. ULs, like DIV elements are block level elements and react similarly to the addition of overflow styles. We'll add the following CSS styles:

CSS style to clip the list
ul.value_list {
    overflow-y: scroll;
    list-style: none;
    width: 300px;
    height: 100px;
    border: 1px solid #333;
    background: #fff;
}

The result should look like:

Our minimally styled unordered list
  • Apple
  • Apricot
  • Avocado
  • Banana
  • Blueberry
  • Cactus Pear
  • Cantaloupe
  • Cherry
  • Dragon Fruit
  • Fig
  • Grape
  • Guava
  • Honeydew
  • Kiwi
  • Lemon
  • Mango
  • Nectarine
  • Orange
  • Papaya
  • Passion Fruit
  • Peach
  • Pear
  • Persimmon
  • Pineapple
  • Plum
  • Pomegranate
  • Raspberry
  • Star Fruit
  • Strawberry
  • Tangerine
  • Watermelon

Using only the above CSS, you should notice that there in FF, there is a large white gap between the text in the LI and the left border. In IE, there is a large gap between the left edge of the border on the unordered list and the border of the code container. To fix this, we add some remedial margins in the CSS:

CSS styles with adjusted margins
ul.value_list {
    overflow-y: scroll;
    list-style: none;
    width: 300px;
    height: 100px;
    border: 1px solid #333;
    background: #fff;
    margin-left: 0px; /* does not affect FF, IE only */
}

ul.value_list li {
    margin-left: -40px; /* adjustment for FF */
}

* html ul.value_list li {
    margin-left: 0px; /* re-adjust for IE */
}
Our minimally styled unordered list with adjusted margins
  • Apple
  • Apricot
  • Avocado
  • Banana
  • Blueberry
  • Cactus Pear
  • Cantaloupe
  • Cherry
  • Dragon Fruit
  • Fig
  • Grape
  • Guava
  • Honeydew
  • Kiwi
  • Lemon
  • Mango
  • Nectarine
  • Orange
  • Papaya
  • Passion Fruit
  • Peach
  • Pear
  • Persimmon
  • Pineapple
  • Plum
  • Pomegranate
  • Raspberry
  • Star Fruit
  • Strawberry
  • Tangerine
  • Watermelon

While the result looks like a typical multiline select, it is indeed an unordered list which allows us to create a highly customized look and feel (when compared to using select and option elements.

The rest of the script isn't that interesting to be honest :) It's all fairly easy plumbing to handle events and there's that little binary search bit, but it's a very generic implementation of binary search.

6. Wrap Up

In this workshop, we covered general techniques for formatting lists for some unique use cases and also demonstrated how we can create a custom combobox like control using DHTML. This script can be generalized and further improved to allow dynamically hooking up the text boxes to corresponding lists and even to an AJAX framework to dynamically draw results.

7. Resources

Additional resources for further exploration:

RSS 2.0 Atom 1.0 CDF