Unobtrusive connected select boxes - yet another solution approach
Update#2: the third demo allows for preselect states and unlimited levels by using a nested UL and radiobuttons enjoy.
Update: the second demo now allows for preselect states and shows the first dropdown enjoy.
The interface element of connected select boxes (one select box changing the options in the other one) is one of the most used and also most annoying elements of web application design. While it works nicely for anyone having JavaScript enabled and using a mouse it can be a nightmare to use with a keyboard (unless you know that you can expand the whole list with alt+cursor down first).
The other problem is that you make yourself dependent on JavaScript with them and I don’t even want to know what the implementations for screen readers are.
That said, it simply does not die out, people love it. The scary thing is that there are dozens of JavaScript solutions to the problem and almost all of them make you dependent on JavaScript. I’ve tried in the past to come up with solutions for it using for example nested HTML lists in this Alistapart article. What scares me most is that the demo code for a bad solution seems to have ended up on several “Script Library” sites and I get emails coming in thanking me for the great solution!
My personal favourite is keeping the functionality independent of a mouse and JavaScript and using Ajax to populate the second drop down as explained in my book, but that is too complex for a lot of owners of web interfaces.
So today, once again the request for connected select boxes reared its ugly head and my esteemed colleague Bradley Wright took it onto himself to solve the issue (working with this guy does rock - believe me). His approach was to create a massive dropdown with all the information and then use JavaScript to chunk it up. Originally he tried class names on the different options to connect them until he asked me to take a peek and I liked the idea but advocated for optgroups instead (the lesser known select child element).
So, the markup for the non-JavaScript version is the following:
<select id="ori">
<optgroup label="Foo1">
<option value="foo1">Foo1</option>
<option value="foo1_1">Foo1_1</option>
<option value="foo1_2">Foo1_2</option>
<option value="foo1_3">Foo1_3</option>
<option value="foo1_4">Foo1_4</option>
<option value="foo1_5">Foo1_5</option>
<option value="foo1_6">Foo1_6</option>
</optgroup>
<optgroup label="Foo2">
<option value="Foo2">Foo2</option>
<option value="Foo2_1">Foo2_1</option>
<option value="Foo2_2">Foo2_2</option>
<option value="Foo2_3">Foo2_3</option>
<option value="Foo2_4">Foo2_4</option>
<option value="Foo2_5">Foo2_5</option>
<option value="Foo2_6">Foo2_6</option>
</optgroup>
<optgroup label="Foo3">
<option value="Foo3">Foo3</option>
<option value="Foo3_1">Foo3_1</option>
<option value="Foo3_2">Foo3_2</option>
<option value="Foo3_3">Foo3_3</option>
<option value="Foo3_4">Foo3_4</option>
<option value="Foo3_5">Foo3_5</option>
<option value="Foo3_6">Foo3_6</option>
</optgroup>
<optgroup label="Foo4">
<option value="Foo4">Foo4</option>
<option value="Foo4_1">Foo4_1</option>
<option value="Foo4_2">Foo4_2</option>
<option value="Foo4_3">Foo4_3</option>
<option value="Foo4_4">Foo4_4</option>
<option value="Foo4_5">Foo4_5</option>
<option value="Foo4_6">Foo4_6</option>
</optgroup>
</select>
All we need now to turn this into connected select boxes is the folllowing:
- Create a new select element
- Loop through all optgroups and create a new select box for each and hide them
- Loop through the options collection of each optgroup
- Take the first option and add it to the new main select element
- Move the other options to the select box connected with this optgroup
- Apply an onchange handler that reads the selectedIndex value of the main select element and shows the select box with the same number, hiding the previous one
- Apply the correct secondary name to the currently shown select box
- Remove the original select box
- Apply the name and ID of the original to the new main select box
Another option is of course to store the data in an object and re-populate the secondary select box every time the first is changed. However, I wanted to avoid that to speed things up.
Check out the example of this technique and tell me what you think.

March 17th, 2007 at 1:10 am
It’s an excellent idea chaps. I wonder if it would be better to rewrite the original checkbox based on the optgroup labels rather than having a base element ‘Foo1′, ‘Foo2′, etc. It seems like you don’t actually want anyone to select those really.
Why not have the first script created select box take the values of the labels on the optgroups and the second one display as is?
March 17th, 2007 at 2:06 am
Very cool. We will use this technique in our current project.
March 17th, 2007 at 10:07 am
When I load the demo page I don’t see the second select list until I make a selection from the first one. (I’m using FF2 on Windows XP)
March 17th, 2007 at 12:40 pm
Christian: Nice work. `OPTGROUP` is simply perfect for this application… it’s a great example of distilling exactly what markup maps to the central problem that you’re trying to solve, and extending it into a hi-fi interface in an unobtrusive manner.
Tom: Without that “FooX” option, users without JavaScript wouldn’t be able to replicate the experience of choosing an item from the initial dropdown box while leaving the second dropdown blank.
That might or might not be preferable, depending on your needs. :)
March 17th, 2007 at 1:44 pm
That’s excellent, very elegant, I like it a lot. It’s too bad you can’t nest
optgroups though, because it would have been the perfect solution!March 19th, 2007 at 4:32 am
Pretty clever. I bet you know this already, but the keyboard navigation is a bit funky, though, at least in FF 2.0.0.2. Put focus on the first element, [tab], and my focus box jumps to the submit element, so I have to [shift]-[tab] to get the second select element. If, after the second element is already created, I tab back to the first one and change it and then my focus is gone, and [tab] again brings focus on the first select, again. Minor, and easily remedied.
Ah, works as expected in IE6 & 7.
March 24th, 2007 at 3:51 pm
March 30th, 2007 at 8:56 am
Thank you for helping us to do things better.
I tried your code with JavaScript enabled, without it and, with JavaScript enabled and CSS disabled.
The last one did not work as expected.
I have just bought your book so is too soon for me to give my own solution.
April 4th, 2007 at 12:08 pm
In response to Tom’s comment, I needed an initial Select based on the Optgroup label as well, and managed to modify the second demo scripts code to do this.
Its a matter of four additional lines of code in the main loop - it should be pretty obvious where these go if you view source on the seond demo and compare:
splitSelect.selects.push(news);
var k=0;
/* Use optgroup labels for first select */
var v = o[0].getAttribute(’value’); var l = g[i].getAttribute(’label’); o[0].setAttribute("value", v);
o[0].innerHTML = l;
/*End modification*/
c.appendChild(o[0]);
Hope this helps anyone else with the same aim.
April 18th, 2007 at 9:46 pm
Cool. But yeah, I guess it won’t work if you need more than one level of nesting, or if a top level should be selectable. Still cool though.
April 19th, 2007 at 8:31 am
This is an excellent idea, but I have a major problem with it: Without Javascript you can select “Foo2″, but with Javascript enabled you can’t.
<optgroup label=”Foo2″>
<option value=”Foo2″>Foo2</option>
It appears it requires the first option after an optgroup to be the same as the optgroup label. The select box I have to try this technique on, my optgroups are section headings, and not meant to be selectable.
Perhaps richs modification deals with this problem, but I can’t use it because it uses innerHTML(), which is not part of any standard DOM.
April 19th, 2007 at 11:25 am
@Soren “I can’t use it because it uses innerHTML(), which is not part of any standard DOM.“
It might not be part of the W3C DOM spec but why does that matter in the real world where all significant browsers support it? If that’s some kind of company policy then I’d be looking for a job somewhere they don’t try to suffocate me with bizarre regulations.
April 19th, 2007 at 1:53 pm
Really good idea. Previously to get around this I just reloaded the page with different GET variables. Nice work I will have to give it a try.
April 19th, 2007 at 3:24 pm
April 19th, 2007 at 4:29 pm
Nice.
Semantics + Accessibility + Progressive Enhancement gets me all hot and bothered!
April 19th, 2007 at 8:52 pm
April 19th, 2007 at 11:19 pm
The keyboard accessibility doesn’t work in Firefox on OSX. I haven’t had a chance to look at what might be the issue, but figured I’d mention it in case you didn’t have access to a mac.
April 20th, 2007 at 8:46 am
@Jerome “It might not be part of the W3C DOM spec but why does that matter in the real world where all significant browsers support it?”
You are not thinking long-term. In 2002 I had to clean up a lot of javascript from 1997 that checked for the browser being IE4 or Netscape4, and assumed you had an old browser if it wasn’t one of these. That gave me a healthy dislike of anything non-standard.
The thing is that webbrowsers parse HTML into a tree structure in memory. If you use the standard DOM API, you manipulate the tree directly, but if you use document.write() or innerHTML, you force the browser to reparse the document and rebuild the tree structure. This is slow.
There is a standard called XHTML. It is HTML written as XML. It can be served as two content-types. Text/html or application/xhtml+xml. If you use the latter, Firefox will use the XML parser, that is much faster, but less tolerant of syntax errors. Faster is good, but it comes at the expense that document.write() et al are disabled. Why? Because they force the browser to treat the page as SGML.
So, are you going to require of your employer that all their webpage in all future be served as text/html just because you like non-standard innerHTML?
April 20th, 2007 at 9:12 pm
April 21st, 2007 at 11:24 pm
April 23rd, 2007 at 8:14 pm
April 25th, 2007 at 9:02 am
April 28th, 2007 at 9:33 am
Lovely solution and just in time for a project I’m working on.
Now I just wish I was smart enough to abstract it so I can use it more than once on a page…
May 7th, 2007 at 12:34 pm
May 9th, 2007 at 2:25 am
May 15th, 2007 at 1:05 pm
May 15th, 2007 at 2:48 pm
May 15th, 2007 at 7:29 pm
May 22nd, 2007 at 12:39 am
May 29th, 2007 at 8:49 pm
July 2nd, 2007 at 11:10 pm
July 9th, 2007 at 12:03 pm
Eine Technik zur Erzeugung eines Inhaltsverzeichnisses mit CSS.
Unobtrusive connected select boxes - yet another solution approach - Wait till I come!
DOM-Script-b [...]
July 9th, 2007 at 12:03 pm
July 9th, 2007 at 1:23 pm
July 9th, 2007 at 3:32 pm
July 9th, 2007 at 4:18 pm
July 10th, 2007 at 3:25 am
July 12th, 2007 at 3:43 am
Unobtrusive connected select boxes - yet another solution approach - Wait till I come!
A DOM-Script [...]
July 12th, 2007 at 8:10 am
July 13th, 2007 at 11:38 am
July 27th, 2007 at 12:16 pm
August 27th, 2007 at 6:27 pm
hi
great job!
i try to adapt Update#2 to use a href instead of input-fields. the first menu is parsed but i have some probs to get a proper unlimitedSelect.parents[n] in the dochange:function (tried to use random ids instead of values).
chris
October 8th, 2007 at 2:45 am
nice.. great job! this is a very useful article! keep it up! thanks!