Dynamic Linked List Boxes: Categories and Entries

Movable Type is being used to store a list of classes, and we need to make those class titles available in a form field (class registration) as a dynamically linked list box. The user will be presented with two list boxes, the first with a list of categories, the second (which is disabled until a category is chosen) with a dynamically generated list of the titles in the selected category.

Ingredients

We’re going to need two important things here. First, a handy DHTML script from Xin Yang called Chained Selects that’s going to power our linked list boxes. The script that you’ll be dropping into your directory is called chainedselects.js.

Second, a javascript file that we’ll call to from our form for the list of categories and entry titles. Movable Type will be generating this file.

Pros and Cons

Dealing with multidimensional data can sometimes be harrowing, especially if you’ve got alot of categories and entries, which we do in this example. The beauty of this process is that once we create the basic structure of the data file, Movable Type will handle all the complexity. Your users get a simple yet effective drop down menu.

There are some weaknesses to using DHTML, namely problems for those who don’t have javascript support; however, other options (that don’t involve massive programming skills) require all the data to be included in the HTML, which is really out of the question considering the amount of data we’re dealing with. But we’ll deal with some of those concerns at the end of the article.

The JavaScript

Our first step is to download the Chained Select script. The only file you’ll need from the .zip file (as mentioned before) is chainedselects.js. Upload this to the same directory your form will be in.

Next, we’re going to use Movable Type to generate a javascript data file. It’s easier than it sounds. Log into your MT installation and create a new index template. We’re going to name is ‘classes.js’ because in our example it’s a list of classes. You can name it whatever you want, just be sure it has a .js extension and remember the name later when you’re calling to it from the HTML.

We’re not going to need HTML headers because this file is basically just going to be generating/populating our menus:

// var hide_empty_list=true; //uncomment this line to hide empty selection lists
var disable_empty_list=true; //uncomment this line to disable empty selection lists
addListGroup("titles", "cat");
addOption("cat", "Select a category", "", "", 1); //Empty starter option
<MTCategories>
    addList("cat", "<$MTCategoryLabel encode_js="1"$>", "<$MTCategoryLabel encode_js="1"$>", "<$MTCategoryLabel encode_js="1"$>");
</MTCategories>

<MTCategories>
    addOption("<$MTCategoryLabel encode_js="1"$>", "Select class", "", "", 1); //Empty starter option
    <MTEntries>
        addOption("<$MTCategoryLabel encode_js="1"$>", "<$MTEntryTitle encode_js="1"$>", "<$MTEntryTitle encode_js="1"$>");
    </MTEntries>
</MTCategories>

The two options at the beginning of the script var hideemptylist=true; and disableemptylist=true; allow you to hide or disable the second list (respectively) if a category has not been chosen. I’ve simply disabled it, as a disappearing/reappearing form element can be confusing to users and a headache to designers. A // starts a comment in a script file, so if we want the browser to recognize this line, we’ll just remove it.

There are three functions (defined in chainedselects.js) that we’re executing in this script: addListGroup, addList and addOption. The addListGroup function creates the “root” group, almost like a root level directory. In this example, we’ll only be using one, because we’re only creating one set of selection boxes. The addList function defines lists so we can attach options to each list. Think of this function as a “folder” into which we’ll place options. The addOption function is then creating options to attach to each created list, much like “files” in a “folder”.

Don’t lose me. Let’s get back to the code: After the initial commented/uncommented options, we’re creating our “root” list. This is essentialy a name for the list. The function addListGroup(“titles”, “cat”); will create a list group called ‘titles’ that we can call from the HTML and ‘cat’ will be the name we’ll use for the primary list box. Don’t get confused. This isn’t the name we’re using in the HTML, this is what we’ll use to add options to that primary list box next.

Now we start to add options to our ‘cat’ list: addOption(“cat”, “Select a category”, “”, “”, 1); //Empty starter option. This is just a dummy option that shows “Select a category” if they’ve not yet chosen one.

<MTCategories>
    addList("cat", "<$MTCategoryLabel encode_js="1"$>", "<$MTCategoryLabel encode_js="1"$>", "<$MTCategoryLabel encode_js="1"$>");
</MTCategories>

If you’ve followed me this far, this bit of code should make sense. We’re basically adding options (and in this case, because it’s the primary list, sub-lists) to the category (‘cat’) list. You’ll notice <$MTCategoryLabel encode_js="1"$> three times in a row. Here’s the breakdown: addList(“first-list-name”, “option text”, “option value”, “sub-list-name”, default-selected). First, we identify that these options (all the categories from your blog generated by MT) all belong on the ‘cat’ list. Next, we use the <$MTCategoryLabel encode_js="1"$> tag to define our option text (what the user sees), the options value (what gets passed to your form) and the sub list name, for adding options later in the code. Please be sure to use encode_js=”1” in your <$MTCategoryLabel$> tag! Your entry titles and javascript don’t usually get along (especially if you like ampersands in your titles).

<MTCategories>
    addOption("<$MTCategoryLabel encode_js="1"$>", "Select class", "", "", 1); //Empty starter option
    <MTEntries>
        addOption("<$MTCategoryLabel encode_js="1"$>", "<$MTEntryTitle encode_js="1"$>", "<$MTEntryTitle encode_js="1"$>");
    </MTEntries>
</MTCategories>

This is probably the most confusing part, but really shouldn’t be. We are using the <MTCategories> to create populate the second list, the one that’s dynamically linked to the first. We first defined our sub lists, now we are adding options (notice the addOptions?) to those sub lists. Please notice that we are adding options and not lists, because we’re done with sub lists. This script is powerful enough to create more sub-lists, but for the sake of what we’re trying to accomplish, two levels is enough.

The <MTCategories> envelope will loop through all of our categories, and the <MTEntries> envelope will loop through all the entries in each category. We’ve put a addOption(“<$MTCategoryLabel encode_js="1"$>”, “Select class”, “”, “”, 1); //Empty starter option before the <MTEntries> envelope because we’re creating a dummy selector for each category. Then we loop through all the entries in that category and add each class title as an option. Note: If you recall the breakdown for our syntax, addOption(“first-list-name”, “option text”, “option value”, default-selected), you can fill in the option value with whatever MT tag you’d like, entry ID, keywords, predefined user field. This gives you some real flexibility and power.

The HTML

Almost done, promise.

We need to do three important things to the HTML file we want the dynamic linked list boxes to appear in. First, in the header of the HTML we need to simply call to the chainedselect.js file and our MT generated javascript file. For instance, something like this should appear in the header of your code, where ‘classes.js’ is the name of the MT index file you created earlier:

<script language="javascript" src="/chainedselects.js"></script>
<script language="javascript" src="/classes.js"></script>

Next, we need to add an onLoad to the body tag so we can associate the data from our MT-generated script file with our menus when the page loads:

<body onload="initListGroup('titles', document.forms[0].Category, document.forms[0].Course, 'cs')">

First, you’ll notice ‘titles’. This was the list group we created at the beginning of our script file. Next, we’re initializing both of our list boxes. If you’re creating deeper (third or fourth level) list heirarchies, you’ll have a document.forms[0].SelectNameGoesHere for each list box. I’ve named the two I have ‘Category’ and ‘Course’, but you can name them what you want; just make sure they match with the names of the list boxes in your form. The ‘cs’ is an additional feature of the chainedselects.js script that uses a small cookie to remember the last selected entry in case the user reloads the page. Consult the documentation included with the .zip file for more information about that.

Lastly, we’re going to put our list boxes into our HTML:

<label for="Category">Category:</label>
<select name="Category" value=""></select>

<label for="Course" class="mandat">Course:</label>
<select name="Course" value=""></select>

Don’t forget that these need to go inside a form. What you do with them (pass data to an email, search form, URL jump, etc.) is completely up to you.

Caveats

As we said earlier, there are some weaknesses behind a form like this. Someone who disables javascript will be faced without a second list box. I’ve worked around that by allowing users to also register directly from an individual entry, in essence giving the users another way (though longer) to accomplish the same thing. That’s why I can risk a form that doesn’t work.

The other option, as is outlined by A List Apart in an article called Complex Dynamic Lists gives a straight HTML solution, which may be more appropriate for pages with less data to incorporate (menus, etc.) If anyone has comments/suggestions about integrating MT with the ALA menu structure or perhaps a better solution via PHP, please feel free to share. It will bide my reader’s over until I can come up with an article about it.

Example In Action

You can see this in action at http://www.compvisions.com/register.php — the list boxes in the registration form filled dynamically with the most current class titles via Movable Type.