Hack 16. Extend an Existing Selection List


Give browser users the option to modify an existing list before making and submitting their choices.

Imagine that you have a list of U.S. states, as in the select element used in "Submit Selection-List Values to the Server Without a Round Trip" [Hack #14]. As part of the customer-registration process, you ask what state your customers live in (for sales-tax purposes, say). However, you want to be able register customers from other countries too, because your product can now be distributed overseas. You do not want to include every country on earth in the select list, though, both for geo-political reasons (countries frequently change, as in the case of the former Yugoslavia) and because the select list would be too big to fit nicely on the page. Thus, you want your users to be able to choose (when applicable) a continent, making a selection that adds a subset of select options to the page. Your application will pass the name of the selected continent to the server program and query the server for the specific countries associated with that continent.

To begin with, you'll provide a select list of continents. When the user makes a selection, the names of the countries within that continent are derived from the server and automatically added to an existing select list, without the page being refreshed. Figure 2-8 shows the web page for this hack, which is based on the previous hack containing the select list of U.S. states.

Figure 2-8. Add options to a list


The user selects a continent in the top-level select list. This action triggers the onclick event for the select element. Here is the HTML code for the page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"         "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head>     <meta http-equiv="content-type" content="text/html; charset=utf-8" />     <script type="text/javascript" src="/books/4/254/1/html/2/js/hacks2_6.js"></script>     <link rel="stylesheet" type="text/css" href="/css/hacks.css" />     <title>Alter select lists</title> </head> <body> <h3>Add Entries to a Select List</h3> <form action="javascript:void%200">     <table border="0">         <tr><td>Add your country: <select  name="_continents">             <option value="southam">South America</option>             <option value="euro">Europe</option>         </select></td></tr><tr><td>Choose one or more states: </td>         <td> <select  name="_state" multiple="multiple" size="4">             <option value="al">Alabama</option>             <option value="ak">Alaska</option>             <option value="az">Arizona</option>             <option value="ar">Arkansas</option>             <option value="ca">California</option>             <option value="co">Colorado</option>             <option value="ct">Connecticut</option>             <option value="de">Delaware</option>             <option value="dc">District of Columbia</option>             <option value="fl">Florida</option>             <option value="ga">Georgia</option>             <option value="hi">Hawaii</option>             <!--snipped here...-->         </select></td></tr>     </table> </form> </body> </html>

All of the JavaScript appears in the file hacks2_6.js. Here are the contents of this file (omitting the creation and initialization of the request object, which the first hack in this chapter and several other hacks show):

var origOptions = null; var request=null; /* Set up the onclick event handler for the "countries"  select list */ window.onload=function(  ){     var sel = document.getElementById("cts");     var sel2 = document.getElementById("sts");     if(sel != null){         sel.onclick=function(  ){             addCountries(this)};     }     origOptions = new Array(  );     //save the original select list of states so that     //it can be reconstructed with just the original states      //and the newly added countries     for(var i = 0; i < sel2.options.length; i++){         origOptions[i]=sel2.options[i];     } } function addCountries(obj){     if (obj == null ) { return; }     var url = "";     var optsArray = obj.options;     var val = "";     for(var i=0; i < optsArray.length; i++){         if(optsArray[i].selected) {             val=optsArray[i].value; break;         }     }     url = "http://www.parkerriver.com/s/selectl?countryType="+             encodeURIComponent(val);     httpRequest("GET",url,true); } //event handler for XMLHttpRequest function handleResponse(  ){     try{         if(request.readyState == 4){             if(request.status == 200){                 var resp =  request.responseText;                 if(resp != null){                     //return value is an array                     var objt = eval(resp);                     addToSelect(objt);                 }             } else {                 //request.status is 503 if the application isn't available;                 //500 if the application has a bug                 alert(                         "A problem occurred with communicating between"+                         " the XMLHttpRequest object and the server program.");             }         }//end outer if     } catch (err) {         alert("It does not appear that the server "+               "is available for this application. Please"+               " try again very soon. \\nError: "+err.message);     } } /* Take an array of string values (obj) and add an option for each of the values to a select list */ function addToSelect(obj){     //contains the U.S. states     var _select = document.getElementById("sts");     var el;     //first remove all options, because the select could include     //newly added countries from previous clicks     while(_select.hasChildNodes(  )){         for(var i = 0; i < _select.childNodes.length; i++){             _select.removeChild(_select.firstChild);         }     }     //now add just the original options: 52 states     for(var h=0; h < origOptions.length;h++) {         _select.appendChild(origOptions[h]);     }     //obj is an array of new option values     for(var i=0; i < obj.length;i++) {         el = document.createElement("option");         el.appendChild(document.createTextNode(obj[i]));         _select.insertBefore(el,_select.firstChild);     } } /* Create and initialize a request object; see Hack #1 or #2. */

When the browser first loads the web page, the code defines an onclick event handler for the select list containing the U.S. states. This event is triggered whenever users click on the select widget, whether or not they change the value in the list. The event handler calls a function named addCountries( ), passing in as a parameter a reference to the select object that was clicked:

window.onload=function(  ){     var sel = document.getElementById("cts");     var sel2 = document.getElementById("sts");     if(sel != null){         sel.onclick=function(  ){                 addCountries(this)};     }     origOptions = new Array(  );     //save the original select list of states so that     //it can be reconstructed with just the original states      //and the newly added countries     for(var i = 0; i < sel2.options.length; i++){         origOptions[i]=sel2.options[i];     } }

The code also saves the original contents of the U.S. states list in an Array object. Otherwise, as the user clicked in the upper select list, the same countries could be added to the second select list over and over again. Because the origOptions Array variable caches the original list, each time the user clicks the top-level select list, the bottom select list is rebuilt with the new countries added in front of the original list of states.

Next up is the addCountries( ) function. You don't need to show this function again, because what it accomplishes is fairly simple. The function cycles through the options in the continents select list, and if an option is checked (i.e., is the selected option), its value is submitted to a Java servlet. The servlet program returns an array of countries associated with the continent, and the code adds those countries to the other select list.

My apologies to all those other global citizens who are not represented by these continent choices. For the sake of brevity, I stopped at Europe and South America. A "real-world" (pun intended!) application would represent all of the world's continents, except perhaps Antarctica.


Figure 2-9 shows the web page after the user has chosen South America.

Figure 2-9. Add countries to the select list without a round trip


New Select List or Mirage?

The code receives the return value in as a string that can be converted to a JavaScript array. The return value takes the form of ["Brazil", "Ecuador",etc.]. It is a string that is evaluated as a JavaScript array using the eval( ) function. In the next step, as if by magic, the new countries appear at the top of the second select list. Here is the responsible addToSelect( ) function:

function addToSelect(obj){     //contains the U.S. states     var _select = document.getElementById("sts");     var el;     //first remove all options, because the select could include     //newly added countries from previous clicks     while(_select.hasChildNodes(  )){         for(var i = 0; i < _select.childNodes.length; i++){             _select.removeChild(_select.firstChild);         }     }     //now add just the original options: 52 states     for(var h=0; h < origOptions.length;h++) {         _select.appendChild(origOptions[h]);     }     //obj is an array of new option values     for(var i=0; i < obj.length;i++) {         el = document.createElement("option");         el.appendChild(document.createTextNode(obj[i]));         _select.insertBefore(el,_select.firstChild);     } }

This function involves basic DOM API programming, representing a select list as a parent node of several option-related child nodes. First, the code clears the select list and repopulates it with the original states. This is a rule for the application; the user can add new countries on top of the original list, but the countries won't pile up in the list repetitively. The code then creates a new option element for each member of the array derived from the server, which is a country name (such as "Brazil"). Finally, the code uses the Node.insertBefore( ) method to insert each new option before the first option in the select list.

The _select.firstChild node keeps changing in the for loop. For example, if Alabama is at the top of the list, _select.firstChild returns the option node containing the "Alabama" value. The loop then inserts "Brazil" before "Alabama," and the option representing Brazil becomes the firstChild node.


Hacking the Hack

Naturally, the next step in this hack is to allow the user to dynamically submit the new country name from the second select element. "Submit Selection-List Values to the Server Without a Round Trip" [Hack #14] shows you how to add this behavior to a select list.




Ajax Hacks
Ajax Hacks: Tips & Tools for Creating Responsive Web Sites
ISBN: 0596101694
EAN: 2147483647
Year: 2006
Pages: 138

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net