How To Create Optgroups in JavaScript
Let’s say you have a select menu on your page, and you want to group related options together. Luckily there’s a handy tag called <optgroup>. It works like this:
Notice how those Breakfast, Lunch, and Dinner headers group everything together so nicely? Now that’s what I call usability!
Here’s what the XHTML looks like:
<select>
<optgroup label="Breakfast">
<option>Cereal</option>
<option>Eggs</option>
<option>Toast</option>
</optgroup>
<optgroup label="Lunch">
<option>Sandwich</option>
<option>Soup</option>
</optgroup>
<optgroup label="Dinner">
<option>Meat</option>
<option>Potatoes</option>
<option>Vegetables</option>
</optgroup>
</select>
Easy enough to do statically, but what if you want to generate optgroups with JavaScript? I looked far and wide on the InterWeb for a way to create optgroups dynamically, and while there are countless examples of JavaScript-driven select menus, I found surprisingly little about dynamically-generated optgroups.
After some exploration and experimentation, I discovered the secret: DOM manipulation. Read on for the gory details.
Step One: Target a Select Menu
First, I need to create a select menu with an id attribute. This will be the parent node I’ll target to add my optgroups. While I could add it to the DOM dynamically, I find it easier to insert it into the XHTML doc, like so:
<select id="foodOptions"></select>
Step Two: Create the OptGroups
Now that I have an empty select menu, how do I populate it? I first need to create the new optgroups, which I can do with the “createElement()” DOM method. In my <script> tag, I create each optgroup and assign it to a variable:
var breakfast = document.createElement("optgroup");
breakfast.label = "Breakfast";
var lunch = document.createElement("optgroup");
lunch.label = "Lunch";
var dinner = document.createElement("optgroup");
dinner.label = "Dinner";
Notice how I’m also setting the label property for each optgroup, which is the text the user sees in the menu.
Step Three: Create the Options
Next, I create some options and attach them to my new optgroups:
var cereal = document.createElement("option");
cereal.value = "cereal";
cereal.appendChild(document.createTextNode("Cereal"));
breakfast.appendChild(cereal);
var eggs = document.createElement("option");
eggs.value = "eggs";
eggs.appendChild(document.createTextNode("Eggs"));
breakfast.appendChild(eggs);
// and so on (repeat for remaining options: Toast, Sandwich, Soup, etc.)
As you can see, there are four steps for each option:
- Create a new “option” element in the DOM.
- Give the option a value (this is what will be sent to the server when the user submits the form).
- Append a new child text node to the option for the text that will be shown in the menu.
- And finally, append the new option to the appropriate optgroup.
One last note here: if you don’t specify a selected item, the first option in the menu will be selected by default (”Cereal” in this case). If you want a different default, you can set it like this:
eggs.selected = true;
Step Four: Attach the OptGroups
Now I have three fully-populated optgroups, but they are floating around somewhere in DOM-space. They need to be attached to the select menu before they will show up on the page. Let’s grab that menu:
var selectMenu = document.getElementById("foodOptions");
One gotcha: be sure this line gets called after the select tag has been created in the DOM. If you call it too early (before the page has finished loading, for instance) you will get a “foodOptions is not defined” error.
Next, I should clear out any old options that might already be cluttering up my select menu. This is not strictly necessary if I know for sure the menu is empty (or if I want to append new options to it), but it’s a good idea just in case:
while (selectMenu.hasChildNodes()) {
selectMenu.removeChild(selectMenu.firstChild);
}
Here’s the last step, attaching my three optgroups to the select menu:
if (breakfast.hasChildNodes()) { selectMenu.appendChild(breakfast); }
if (lunch.hasChildNodes()) { selectMenu.appendChild(lunch); }
if (dinner.hasChildNodes()) { selectMenu.appendChild(dinner); }
Notice how I’m first checking to see if each optgroup is empty before appending it to the menu? Safari will automatically hide empty optgroups, but both Firefox and IE show them as a label with no options under it, which you probably don’t want to see.
Putting It All Together
Here’s the finished product:
And here’s the complete code:
<select id="foodOptions"></select>
<input type="button" value="Show me the menu!"
onclick="populateSelect();" />
<input type="button" value="Clear it"
onclick="var f = document.getElementById('foodOptions');
while (f.hasChildNodes()) { f.removeChild(f.firstChild); }" />
<script>
function populateSelect() {
// create optgroups
var breakfast = document.createElement("optgroup");
breakfast.label = "Breakfast";
var lunch = document.createElement("optgroup");
lunch.label = "Lunch";
var dinner = document.createElement("optgroup");
dinner.label = "Dinner";
// create options and attach to optgroups
var cereal = document.createElement("option");
cereal.value = "cereal";
cereal.appendChild(document.createTextNode("Cereal"));
breakfast.appendChild(cereal);
var eggs = document.createElement("option");
eggs.value = "eggs";
eggs.appendChild(document.createTextNode("Eggs"));
breakfast.appendChild(eggs);
var toast = document.createElement("option");
toast.value = "toast";
toast.appendChild(document.createTextNode("Toast"));
breakfast.appendChild(toast);
var sandwich = document.createElement("option");
sandwich.value = "sandwich";
sandwich.appendChild(document.createTextNode("Sandwich"));
lunch.appendChild(sandwich);
var soup = document.createElement("option");
soup.value = "soup";
soup.appendChild(document.createTextNode("Soup"));
lunch.appendChild(soup);
var meat = document.createElement("option");
meat.value = "meat";
meat.appendChild(document.createTextNode("Meat"));
dinner.appendChild(meat);
var potatoes = document.createElement("option");
potatoes.value = "potatoes";
potatoes.appendChild(document.createTextNode("Potatoes"));
dinner.appendChild(potatoes);
var vegetables = document.createElement("option");
vegetables.value = "vegetables";
vegetables.appendChild(document.createTextNode("Vegetables"));
dinner.appendChild(vegetables);
// set "eggs" as the default
eggs.selected = true;
// clear select menu and append optgroups
var selectMenu = document.getElementById("foodOptions");
while (selectMenu.hasChildNodes()) {
selectMenu.removeChild(selectMenu.firstChild);
}
if (breakfast.hasChildNodes()) { selectMenu.appendChild(breakfast); }
if (lunch.hasChildNodes()) { selectMenu.appendChild(lunch); }
if (dinner.hasChildNodes()) { selectMenu.appendChild(dinner); }
}
</script>
Posted on September 22nd, 2007 in the JavaScript category |
Permalink
You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
Leave a Reply
You must be logged in to post a comment.

