Generate immediate interaction with a server program when the browser user clicks a checkbox. Checkboxes are those little squares or buttons that allow users to make choices among multiple options. The conventional setup is for users to check one or more checkboxes as part of a form that they have to submit later. But what if you want your application to submit only the checkbox values, rather than the whole form, and have that submission take place when the user clicks the checkbox and not at some indeterminate time in the future? This hack represents a poll in which users vote for their favorite team and individual sports. When the browser user selects any of the checkboxes, this action triggers an event that submits this value to a server program and then displays the poll results. Figure 2-10 shows what the page looks like in a browser. Figure 2-10. Choose your favorite sportsThe server program has a database that captures the poll results; the program updates and then returns those results. This hack uses the XMLHttpRequest object to send the sport choices and handle the server's response, and it uses DOM programming and CSS to display the poll results. 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_5.js"></script> <link rel="stylesheet" type="text/css" href="/css/hacks2_5.css" /> <title>submit checkbox values</title> </head> <body> <h3>Choose your favorite sports</h3> <h4>Team sport</h4> <form action="javascript:void%200" method="get"> <div > <input type="checkbox" name="team_sports" id= "baseball" value="baseball" /> Baseball <br /> <input type="checkbox" name="team_sports" id= "soccer" value="soccer" /> Soccer <br /> <input type="checkbox" name="team_sports" id= "football" value="football" /> Football <br /> <input type="checkbox" name="team_sports" id= "basketball" value="basketball" /> Basketball <br /> <input type="checkbox" name="team_sports" id= "lacrosse" value="lacrosse" />Lacrosse <br /> <input type="checkbox" name="team_sports" id= "hockey" value="hockey" /> Hockey <br /> <input type="checkbox" name="team_sports" id= "tennis" value="tennis" /> Tennis <br /> </div> </form> <div > <span ></span> <span ></span></div> <h4>Individual sport</h4> <form action="javascript:void%200" method="get"> <div > <input type="checkbox" name="individual_sports" id= "cycling" value="cycling" /> Cycling <br /> <input type="checkbox" name="individual_sports" id= "running" value="running" /> Running <br /> <input type="checkbox" name="individual_sports" id= "swimming" value="swimming" /> Swimming <br /> <input type="checkbox" name="individual_sports" id= "nordic_skiing" value="nordic_skiing" />Nordic Skiing <br /> <input type="checkbox" name="individual_sports" id= "inline_skating" value="inline_skating" />Inline Skating <br /> <input type="checkbox" name="individual_sports" id= "triathlon" value="triathlon" />Triathlon <br /> <input type="checkbox" name="individual_sports" id= "track" value="track" />Track <br /> </div> </form> <div > <span ></span> <span ></span></div> </body> </html> This page first imports the JavaScript code that performs all of the application's work from a file named hacks2_5.js. This HTML also imports a stylesheet (hacks2_5.css) to control the page's appearance and makes the poll results invisible until the user is ready to see them. The HTML page includes two div elements, each containing a set of checkbox elements that specify the various team and individual sports. Here is the JavaScript code underlying this hack: var sportTyp = ""; var request=null; window.onload=function( ){ var allInputs = document.getElementsByTagName("input"); if(allInputs != null){ for(var i = 0; i < allInputs.length;i++) { if(allInputs[i].type == "checkbox"){ allInputs[i].onchange=function( ){ sendSportsInfo(this)}; } } } } function sendSportsInfo(obj){ if (obj == null ) { return; } var url = ""; var nme = ""; if(obj.checked) { nme = obj.name; var sub = nme.substring(0,nme.indexOf("_")); sportTyp=sub; url = "http://www.parkerriver.com/s/fav_sports?sportType="+nme+ "&choices="+obj.value; 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 a JSON object var func = new Function("return "+resp); displayPollResults(func( )); } } 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); } } function displayPollResults(obj){ var div = document.getElementById(sportTyp+"_poll"); var spans = div.getElementsByTagName("span"); for(var i = 0; i < spans.length; i++){ if(spans[i].id.indexOf("title") != -1){ spans[i].innerHTML = "<strong>Here are the latest poll "+ "results for "+sportTyp+ " sports</strong>" } else { //use the object and its properties var str ="<br />"; for(var prop in obj) { str += prop + " : "+obj[prop]+"<br />";} spans[i].innerHTML = str; } } div.style.visibility="visible"; }
The first task of this code is to assign a function to execute when the checkbox's state changes (from unchecked to checked). This is the responsibility of the window.onload event handler, which the browser calls after the page has been completely loaded: window.onload=function( ){ var allInputs = document.getElementsByTagName("input"); if(allInputs != null){ for(var i = 0; i < allInputs.length;i++) { if(allInputs[i].type == "checkbox"){ allInputs[i].onchange=function( ){ sendSportsInfo(this)}; } } } } The code first stores an Array of all the page's input elements in an allInputs variable. If the input is of a checkbox type, as in <input type="checkbox" .../>, its onchange property refers to a function that calls sendSportsInfo( ). The code sets all the checkbox's onchange event handlers at once; it will not affect any other input elements a page designer adds to the page later. Using this as a parameter to sendSportsInfo( ) is a handy mechanism for passing a reference to the exact input element whose state has changed. Vote Early and OftenLet's look at the sendSportsInfo( ) function more closely. This function constructs a URL or web address to send the user's sports choices to a server program: function sendSportsInfo(obj){ if (obj == null ) { return; } var url = ""; var nme = ""; if(obj.checked) { formObj=obj; nme = obj.name; var sub = nme.substring(0,nme.indexOf("_")); sportTyp=sub; url = "http://www.parkerriver.com/s/fav_sports?sportType="+nme+ "&choices="+obj.value; httpRequest("GET",url,true); } } Since we used the this keyword as a parameter to sendSportsInfo( ), the obj variable refers to an HTML input element. We are only going to hit the server if the input checkbox is selected, so the code checks for that state. The name of each input element in the form is set in the HTML to team_sports or individual_sports, so the code captures the name and the name substring preceding the "_" character (we need that for the code that displays the poll results).
The URL requires the sport type and the value of the checkbox. A typical URL example looks like http://www.parkerriver.com/s/fav_sports?sportType= individual_sports&choices=soccer. The httpRequest( ) method uses the request object to query the server with these values. Poll VaultThe server returns an HTTP response representing the latest poll results, after it stores the user's vote. The code has designated the handleResponse( ) function for dealing with the response and calling another function to display the results: if(request.readyState == 4){ if(request.status == 200){ var resp = request.responseText; if(resp != null){ //return value is a JSON object var func = new Function("return "+resp); displayPollResults(func( )); } } } The server returns the result not as XML but in JSON format, a form of plain text that can easily be converted by JavaScript to an object. This is a useful way of enclosing the results. A typical server return value looks like: { nordic_skiing: "0", inline_skating: "0", cycling: "2", track: "2", swimming: "0", triathlon: "0", running: "3" } The code uses the technique described in "Receive Data in JSON Format" [Hack #7] to evaluate this text as a JavaScript object. The code then calls displayPollResults( ), which, as you've probably figured out, shows the results in the browser. Figure 2-11 shows what the results look like in Safari. Figure 2-11. Which sports are favored?The displayPollResults( ) function uses the DOM to generate a colorful display of the results in the browser: function displayPollResults(obj){ var div = document.getElementById(sportTyp+"_poll"); var spans = div.getElementsByTagName("span"); for(var i = 0; i < spans.length; i++){ if(spans[i].id.indexOf("title") != -1){ spans[i]. The poll results are displayed inside div elements, which have ids of team_poll or individual_poll. Each div contains two span elements. The span elements are responsible for the result titles and the actual data. At this point, it is helpful to look at the CSS file that specifies various rules for the appearance of our poll results. The divs and their contents are initially hidden (with the visibility CSS property), until the user clicks a checkbox: .p_title {font-size: 1.2em; color: teal } h3 { margin-left: 5%; font-size: 1.4em; } h4 { margin-left: 5%; font-size: 1.2em; } div.poll { margin-left: 5%; visibility: hidden; border: thin solid black; padding: 2%; font-family: Arial, serif; color: gray; background-color: yellow} div.team { margin-left: 5%; border: thin solid green; padding: 5%; font-family: Arial, serif} div.ind { margin-left: 5%; border: thin solid green; padding: 5%; font-family: Arial, serif } div { max-width: 50% } One of the cool aspects of DOM and Ajax mechanisms is that CSS properties are programmable too. When the page view is ready to show the poll results, the visibility property of the divs that hold these results is set to visible. This is accomplished with the code div.style.visibility = "visible". In the displayPollResults( ) function, the code sets the innerHTML property for the span elements responsible for displaying a title about the poll results. In addition, the poll results derived from the server are stored in a string and displayed in this manner: var str ="<br />"; for(var prop in obj) { str += prop + " : "+ obj[prop]+"<br />";} spans[i].innerHTML = str; The obj variable is a JavaScript object. The for(property in object) expression then generates a string that looks like this: <br />baseball : 2<br />soccer : 3... If you keep clicking on checkboxes, you can watch the votes increment without anything else changing in the browser. This is a useful design for applications that collect discrete feedback from users and instantaneously display the results. |