Skip to content

Categories:

Dependent Lists and Progressive Enhancement

I recently stumbled across a newsgroup post where someone needed dependent list boxes that let users select a car make from one list box and a second list box would be populated with car models. He wanted to use AJAX to get the list of models from the server, but also wanted a solution that would work for users that didn’t have JavaScript (particularly, mobile device users). My suggestion was to use progressive enhancement. The end result can be viewed at http://www.fotiweb.com/samples/dependent-list-box/, though some of this solution is PHP based (see below for that code).

My suggestion was to create a web service that would take a car make and return a list of models. He could interact with this service via an AJAX call, parse the results, and populate the list box with the results. Alternatively, his server side processing could determine if a make had been selected, and if so call the service to get the list and populate the list box that way.

I started by creating a basic HTML form:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
 "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" >
<style type="text/css">
select {
    width: 15em;
}
<title>Untitled</title>
<body>
</body> </html>

I used placeholders where the list options will be.

Next, I created the web service at . Here’s what that service looks like:

<?php
// This example returns just a simple comma separated list.
// A real world example would probably look up values from a
// database and might return a JSON or XML string with the
// results. This is just a proof of concept.
if (!isset($_GET['make'])) {
    // Exit nicely
    exit(0);
}
$make = $_GET['make'];
switch ($make) {
    case 'Ford':
        echo 'Focus,Explorer';
        break;
    case 'Nissan':
        echo 'Sentra';
        break;
    case 'Toyota':
        echo 'Prius,Tundra';
        break;
    default:
        echo '';
        break;
}
exit(0);
?>

Next I needed to create the PHP behind my form page. This PHP populates the vehicle makes and if the user has selected a make and clicked the “Get Models” button, will also populate the models by making a curl request to the web service.

<?php
// The 'makes' list might be populated from a database
$makes = array('Ford', 'Nissan', 'Toyota');
// 'models'
$models = array();
// 'selectedMake' The make selected by the user (if any)
$selectedMake = '';

// Check whether the make has been submitted
if (array_key_exists('getmodels', $_GET)) {
    if ( isset($_GET['make']) ) {
        $selectedMake = $_GET['make'];
        // Request model values for the selected
        $ch = curl_init("http://www.fotiweb.com/samples/dependent-list-box/getModels/?make=$selectedMake");
        // Return the transfer as a string
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        // $output contains the output string
        $models = curl_exec($ch);
        // Close the curl resource
        curl_close($ch);
        // Convert results to array
        $models = explode(',', $models);
    }
}

// Format variables for HTML output
$makeOptionList = '';
$modelsOptionList = '';

// Generate the options list of makes
foreach ($makes as $make) {
    $makeOptionList .= "<option value='$make'";
    if ($selectedMake == $make) {
        $makeOptionList .= " selected='selected'";
    }
    $makeOptionList .= ">$make</option>";
}

// Generate the options list of models
if (count($models) > 0) {
    foreach ($models as $model) {
        $modelsOptionList .= "<option value='$model'>$model</option>";
    }
}
else {
    $modelsOptionList = '<option value="">Select a Make</option>';
}
?>

So now the page is functioning for all users that don’t have JavaScript. When the page loads, the makes will be populated. If the user clicks on the “Get Models” button, the selected make will be submitted back to the PHP above, which in turn will populate the models and show the form again, but this time with the populated results.

All that’s left is to use Progressive Enhancement to provide an AJAX alternative to the post back to the server. An AJAX solution will be more responsive, as the entire page doesn’t need to be reloaded. Here’s the JavaScript code. Note, I’m using the Yahoo UI Library for it’s DOM manipulation, Event handling utility, Element manipulation, and Connection Manager for it’s simplified AJAX interface.


And then my own script:

var make = new YAHOO.util.Element('make'),
    submitbtn = new YAHOO.util.Element('getmodels'),
    models = new YAHOO.util.Element('models');

// Remove the submit button
submitbtn.setStyle('display','none');

// Attach event listeners to 'make' that will call getModels
// service, parse the results, and populate 'models' options
make.on('click', function (e) {
    YAHOO.util.Connect.setForm('getMakeModel');
    var transaction = YAHOO.util.Connect.asyncRequest('GET',
            'http://www.fotiweb.com/samples/dependent-list-box/getModels/', {
                success: function(o) {
                    var i, op, old,
                        modelList = o.responseText;
                    // Remove the existing option elements
                    old = models.getElementsByTagName('option');
                    while (old.length > 0) {
                        old[old.length - 1].parentNode.removeChild(old[old.length - 1]);
                    }
                    // Convert results to array
                    modelList = modelList.split(",");
                    // Generate the options list of models
                    for (i = 0; i < modelList.length; i++) {
                        op = document.createElement('option');
                        op.text = modelList[i];
                        op.value = modelList[i];
                        models.appendChild(op);
                    }
                }
    }, null);
});

This will hide the submit button and perform the request to the getModels service when the user selects a make. This is just one way you could use Progressive Enhancement to enhance your dependent lists.

Posted in Web Development. Tagged with .

Another Baby First

Today my son Alex waved “bye bye” to someone for the first time. I was surprised at how exciting I found it. He’s growing up so quick.

Posted in General.

Google Chrome

When I started heavily using Google apps like GMail, Calendar, and Documents, I decided to try out Google’s Chrome browser. It still has some bugs, but overall I have been very impressed with it.

Posted in General. Tagged with , , , , .

Looking for Work as a Software Engineer

There’s never a good time to be laid off, but I can’t think of a worst time than right before the holidays. With the current economic downturn, its even harder to find work. Now that the holidays are over and the new year is right around the corner, I’m hoping that new positions will open up.

Here is a little bit about me:

Summary of Qualifications
Software Engineer and strong team player with 10+ years of experience, a keen eye for usability, and a proven ability to design and develop code that is clean and easy to maintain.

Technical Skills
Java, JSP, JSTL, Servlets, JavaScript, AJAX, JSON, CSS, HTML/XHTML, Yahoo UI Library (YUI), , C#.NET, PHP, ASP, XML, Spring MVC, Hibernate, Java Persistence API, MySQL, PostgresSQL, C/C++, OOD, Semantic Markup, Progressive Enhancement, Unobtrusive JavaScript, Microformats

If you you know of any companies in the North East that are hiring Software Engineers (or possibly Web Developers), please let me know.

The job hunt continues…

Posted in General. Tagged with , .