October 27, 2016
Hot Topics:

Advanced Features in Creating a Double-Combo Linked List with Ajax

  • January 17, 2006
  • By Dave Crane, Eric Pascarello, and Darren James
  • Send Email »
  • More Articles »

To many of you, we're sure, the more interesting part of the constructor comes in the last two lines. Let's look at each in turn:

this.ajaxHelper = new net.ContentLoader( this, url, "POST",
                          options.requestParameters || [] );

Obviously, we know that our component requires Ajax capabilities. As fortune and a little planning would have it, we already have an object to perform the lion's share of our Ajax-related work—that is, the net.ContentLoader we cleverly refactored earlier. The DoubleCombo simply passes itself (via this) as the component parameter to the ContentLoader helper. The url parameter is also passed through to the helper as the target URL of Ajax requests, and the HTTP request method is specified with the string "POST". Finally, the requestParameters property of the options object, or an empty array if none was defined, is passed as the "constant" parameter array to send with every Ajax request. Also recall that because we passed this as a component argument, the DoubleCombo object is obligated to implement the implied contract with the net.ContentLoader object we discussed earlier. That is, we must implement an ajaxUpdate() and a handleError() method. We'll get to that in a bit, but first let's look at the last line of our constructor:


Finally our constructor is doing something that looks like behavior. Yes, the moment we've all been waiting for: the behavior implementation. Everything we'll do from here on out is directly related to providing double-combo functionality. So without further ado, let's take a look at this method along with all the other DoubleCombo methods that will be required. Thanks to all of the infrastructure we've put in place, our task is far from daunting at this point. Keep in mind that all the methods that appear throughout the rest of the example are assumed to be embedded within a prototype literal object, exactly as we did for the net.ContentLoader implementation.

DoubleCombo.prototype = {
   // all of the methods….

So, let's peek under the hood. First, the initializeBehavior() method is shown here:

initializeBehavior: function() {
   var oThis = this;
   this.master.onchange =
      function() { oThis.masterComboChanged(); };

Short and sweet. This method puts an onchange event handler on the master select element (formerly done in the HTML markup itself). When triggered, the event handler invokes another method on our object, masterComboChanged():

masterComboChanged: function() {
   var query = this.master.options[
   this.ajaxHelper.sendRequest( 'q=' + query );

Wow, also short and sweet. All this method has to do is create a request parameter and send our Ajax request. Since the Ajax-specific work has been factored out into another object, this is a single line of code. Recall that sendRequest() will create and send an XMLHttpRequest, then route the response back to our ajaxUpdate() method. So let's write that:

Click here for a larger image.

This method takes the response XML from the request object and passes it to a method named createOptions(), which creates our slave select's option elements. The method then simply clears and repopulates the slave select element. The createOptions() method, although not part of any public contract, is a helper method that makes the code cleaner and more readable. Its implementation, along with another helper method, getElementContent(), is shown in listing 13.

Listing 13: Combo population methods.

createOptions: function(ajaxResponse) {
   var newOptions = [];
   var entries = ajaxResponse.getElementsByTagName('entry');
   for ( var i = 0 ; i < entries.length ; i++ ) {
      var text = this.getElementContent(entries[i],
      var value = this.getElementContent(entries[i],
      newOptions.push( new Option( text, value ) );
return newOptions;

getElementContent: function(element,tagName) {
   var childElement = element.getElementsByTagName(tagName)[0];
   return (childElement.text != undefined) ? childElement.text :

These methods perform the hard work of actually fetching values from the XML response document, and creating options objects from them. To recap, the XML structure of the response is as follows:

<?xml version="1.0" ?>
      <optionText>Select A Territory</optionText>

The createOptions() method iterates over each entry element in the XML and gets the text out of the optionText and optionValue elements via the getElementContent() helper method. The only thing particularly noteworthy about the getElementContent() method is that it uses the IE-specific text attribute of the XML element if it exists; otherwise it uses the W3C-standardized textContent attribute.

Error handling

We're all finished. Almost. We've implemented all the behaviors needed to make this component fully operational. But, dang, we said we'd handle error conditions, too. You will recall that we have to implement a handleError() method in order to play nicely with the net.ContentLoader. So let's implement that, and then we'll really be finished. So what's the appropriate recovery action if an error occurs? At this point we still can't really say. The application using our DoubleCombo component ultimately should decide. Sounds like a job for our options object—remember the one we passed to the constructor? Let's think about that contract for a second. What if we constructed our double-combo component with code that looks something like this?

function myApplicationErrorHandler(request) {
   // Application function that knows how
   // to handle an error condition

var comboOptions = { requestParameters: [
                    "param1=one", "param2=two" ],
                    errorHandler: myApplicationErrorHandler };

var doubleCombo = new DoubleCombo( 'region',
                                   comboOptions );

In this scenario, we've let the application define a function called myApplicationErrorHandler(). The implementation of this method is finally where we can put application-specific logic to handle the error condition. This could be an alert. Or it could be a much less intrusive "oops" message a la GMail. The point is we've deferred this decision to the application that's using our component. Again, we've provided the mechanism and allowed someone else to provide the semantics. So now we have to write the DoubleCombo object's handleError() method:

handleError: function(request) {
   if ( this.options.errorHandler )

Component bliss

Congratulations are in order! We're finally all done. We have a general component that we can construct with the IDs of any two select elements and some configuration information, and we have instant double-combo capability. And it's just so ... door slams open!

Enter pointy-haired manager, 2:45 P. M. Friday. "Johnson," he says. "We have to support subterritories! … And we need it by Monday morning!" Dramatic pause. "Ouch!" you finally retort. Then you regain your composure and say, "I'll make it happen, sir. Even if I have to work all weekend." He hands you the new page design:

<select id="region"       name="region"><select>
<select id="territory"    name="territory"></select>
<select id="subTerritory" name="subTerritory"></select>

Pointy-hair retreats. You open the HTML page in Emacs, because that's the way you roll. You go directly to the head section. The cursor blinks. You begin to type:

   function injectComponentBehaviors() {
      var opts1 = { requestParameters: "master=region" };
      var opts2 = { requestParameters: "master=territory" };

      new DoubleCombo( 'region',
                       'DoubleComboXML.aspx', opts1 );
      new DoubleCombo( 'territory',
                       'DoubleComboXML.aspx', opts2 );

You press a key that runs a macro to nicely format your code. You save. You exclaim over your shoulder, "I'll be working from home," as you pass by Pointy's office at 2:57. You plop down on the sofa and think to yourself, "Boy, I am a rock star!" Okay, already. Enough of the fantasy. Let's tie a bow around this thing and call it a day.


The double combination select element is an efficient method to create dynamic form elements for the user. We can use JavaScript event handlers to detect changes in one select element and trigger a process to update the values in the second element. By using Ajax, we are able to avoid the long page-loading time that you would see using a JavaScript-only solution. Using Ajax, we can make a database query without the entire page being posted back to the server and disrupting the user's interaction with the form. Ajax makes it easy for your web application to act more like a client application.

With this code, you should be able to develop more sophisticated forms without having to worry about the normal problems of posting pages back to the server. With the ability to extend this script to act on multiple combinations of selection lists, your users can drill down more precisely through several layers of options to obtain the information or products they are looking for.

Finally, we did some refactoring of the code to build ourselves an industrial-strength component to facilitate reuse and customization down the road. From our perspective, we've encapsulated this functionality in a reusable component and won't ever need to write it again. From our users' perspective, they won't be getting that screen that says the product is not available when buying items from our online store. Everybody's happy.

About the Authors

Dave Crane has pushed the boundaries of DHTML, and latterly Ajax, on digital TV set-top boxes, in home automation and banking and financial systems. He lives in Gloucestershire, UK.

Eric Pascarello is an ASP.NET developer and a moderator of the HTML and JavaScript forum at JavaRanch. He lives in Laurel, MD.

Darren James is the architect of the opensource Rico project. He lives in Sunnyvale, CA.

About the Book

Ajax in Action
By Dave Crane, Eric Pascarello, and Darren James

Published: October, 2005, Paperback: 680 pages
Published by Manning Publications Co.
ISBN: 1932394613
Retail price: $44.95
This material is from Chapter 9 of the book.

Page 3 of 3

Comment and Contribute


(Maximum characters: 1200). You have characters left.



Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date
Rocket Fuel