reptile7's JavaScript blog
Wednesday, August 09, 2006
 
Data Validation II
Blog Entry #47

We continue today our introductory treatment of form data validation. In the last entry, we carried out some simple validations of user inputs into text fields. Well, what about other types of form controls, huh?

Don't leave it blank, part 2

We turn now to radio buttons and checkboxes. How do you ensure that a user chooses a radio button or at least one checkbox from a set thereof? You could take the easy way out by preselecting for the user a particular radio button or checkbox via a checked minimized attribute in its <input> tag, e.g.:

<input type="radio" name="radio_button_name" value="button_value" checked>

But perhaps you would rather not do that - what then? You may recall that in Blog Entry #17 we used a for loop to determine a user's radio button or checkbox input, and we can use a for loop again to run through a set of radio buttons or checkboxes to make sure it hasn't been left blank. In both cases, use of a loop is not strictly necessary but substantially lowers the number of lines of code that are required. Consider the demo question below - first, click the Submit button without making a choice, and then choose one of the radio buttons and reclick the Submit button:

Which element below is a member of the halogen family?

Arsenic
Gallium
Iodine
Selenium
Xenon


Here's the code:
<head><script type="text/javascript">
function radio_test( ) {
var unchecked=0;
for (i=0; i<document.fradio.hal.length; i++) {
if (!document.fradio.hal[i].checked) unchecked++;
if (unchecked==5) window.alert("Please make a selection."); }
if (unchecked==4) {
if (document.fradio.hal[2].checked) window.alert("Iodine it is!");
else window.alert("Nope, try again."); } }
</script></head><body>
<form name="fradio">
Which element below is a member of the halogen family?<p>
<input type="radio" name="hal"> Arsenic<br>
<input type="radio" name="hal"> Gallium<br>
<input type="radio" name="hal"> Iodine<br>
<input type="radio" name="hal"> Selenium<br>
<input type="radio" name="hal"> Xenon<p>
<input type="button" value="Submit" onclick="radio_test( );"> <input type="Reset">
</form></body>

Let's briefly look at the radio_test( ) function that is triggered by the Submit button.

The key to the radio_test( ) function is the variable unchecked, which counts the number of unchecked radio buttons. After unchecked is initialized with a value of 0, a for loop runs through the five radio buttons to see which ones are unchecked:

for (i=0; i<document.fradio.hal.length; i++) {
if (!document.fradio.hal[i].checked) unchecked++;
if (unchecked==5) window.alert("Please make a selection."); }

The first if statement above introduces us to the ! logical/Boolean NOT operator, which "[r]eturns false if its single operand can be converted to true; otherwise, returns true," quoting Netscape. For an unchecked radio button, the document.fradio.hal[i].checked expression evaluates to false; the !document.fradio.hal[i].checked condition then returns true, and unchecked is incremented.

If all five radio buttons are unchecked, then the value of unchecked peaks out at 5 in the loop's last iteration; the condition of the second if statement now returns true, and a "Please make a selection" alert( ) message pops up. If the user does check one of the radio buttons (unchecked=4), then an external-to-the-loop if...else conditional generates appropriate alert( ) messages for correct and incorrect answers to the original question.

Only two checkboxes, please

Lurking in the HTML Goodies archives is an interesting and useful "Checkboxes: Only Two" tutorial that, per its title, offers code for permitting a user to choose only two boxes in a set of checkboxes. The script therein, although satisfyingly functional, can itself be significantly condensed via a for loop, as we'll show below. Consider the demo question below - try making three choices and see what happens:

Choose the two elements below that are alkali metals:

Aluminum
Iron
Lithium
Magnesium
Rubidium


Here's my code (adapted from Joe's original code):
<head><script type="text/javascript">
function checkbox_test( ) {
var elemcount=0;
for (i=0; i<document.fcheckbox.alkmet.length; i++) {
if (document.fcheckbox.alkmet[i].checked) elemcount++;
if (elemcount==3)
{window.alert("Only two checkboxes, please."); return false; break;} } }
function answer( ) {
if (document.fcheckbox.alkmet[2].checked && document.fcheckbox.alkmet[4].checked)
window.alert("Excellent!");
else window.alert("Nope, try again."); }
</script></head><body>
<form name="fcheckbox">
Choose the two elements below that are alkali metals:<p>
<input type="checkbox" name="alkmet" onclick="return checkbox_test( );"> Aluminum<br>
<input type="checkbox" name="alkmet" onclick="return checkbox_test( );"> Iron<br>
<input type="checkbox" name="alkmet" onclick="return checkbox_test( );"> Lithium<br>
<input type="checkbox" name="alkmet" onclick="return checkbox_test( );"> Magnesium<br>
<input type="checkbox" name="alkmet" onclick="return checkbox_test( );"> Rubidium<p>
<input type="button" value="Submit" onclick="answer( );"> <input type="Reset">
</form></body>

Clicking any of the checkboxes above triggers the checkbox_test( ) function in the document head script. The key to the checkbox_test( ) function is the variable elemcount, which counts the number of checked checkboxes. After elemcount is initialized with a value of 0, a for loop runs through the five checkboxes to see which ones are checked:

for (i=0; i<document.fcheckbox.alkmet.length; i++) {
if (document.fcheckbox.alkmet[i].checked) elemcount++;
if (elemcount==3)
{window.alert("Only two checkboxes, please."); return false; break;} }

For each checked checkbox, the condition of the first if statement returns true and elemcount is incremented. If three checkboxes are checked, then the condition of the second if statement returns true; an "Only two checkboxes, please" alert( ) message pops up, the value false is returned to the calling checkbox, and the loop is terminated with a break statement. We briefly discussed the return statement and its use in a function body in Blog Entry #23.

In its "Description" of the onClick event handler, Netscape notes, "For checkboxes, links, radio buttons, reset buttons, and submit buttons, onClick can return false to cancel the action normally associated with a click event." So, suppose the user checks the Aluminum, Iron, and Lithium checkboxes, and that false is returned to the Lithium checkbox; consequently, the onclick="return checkbox_test( );" code in the third <input> tag becomes in effect onclick="return false", which cancels the third click event and promptly unchecks the checkbox.

Joe concludes, "You can set this to as many checkboxes as you'd like and as many choices from those boxes as you'd like...Just make sure to keep the return command in the checkboxes themselves." I can confirm that if the return keyword is removed from the onclick="return checkbox_test( );" attribute, then the uncheck-the-checkbox effect does not occur.

Selection lists

We can easily retool the radio_test( ) function above for a selection list to ensure that a user selects an option thereof. Consider demo question #3 below - first, click the Submit button without making a choice, and then choose one of the options and reclick the Submit button:

If you could visit another planet, which one would it be?




Here's the code:
<head><script type="text/javascript">
function select_test( ) {
var unselected=0;
for (i=0; i<document.fselect.planet.options.length; i++) {
if (!document.fselect.planet.options[i].selected) unselected++;
if (unselected==8) window.alert("Please make a selection."); } }
</script></head><body>
<form name="fselect">
If you could visit another planet, which one would it be?<p>
<select name="planet" size="5">
<option>Mercury</option>
<option>Venus</option>
<option>Mars</option>
<option>Jupiter</option>
<option>Saturn</option>
<option>Uranus</option>
<option>Neptune</option>
<option>Pluto</option>
</select><p>
<input type="button" value="Submit" onclick="select_test( );"> <input type="Reset">
</form></body>

As noted in Blog Entry #17, the selected property of the option object is analogous to the checked property of the radio and checkbox objects; the options[i] array is itself a property of the select object.

Note the size="5" in the <select> tag; without it, the selection list would only show the Mercury option, which then by default becomes the user's 'chosen selection.'

A much easier approach is to use the "If you could visit another planet..." question as the 0th and only displaying option of the selection list:

<select name="planet">
<option>If you could visit another planet, which one would it be?</option>
<option>Mercury</option> etc.

In this case, the select_test( ) function can be simplified to:

function select_test( ) {
if (document.fselect.planet.selectedIndex==0) window.alert("Please make a selection."); }

The selectedIndex property of the select object was also discussed in Blog Entry #17.

Finally, my attempts to retool the checkbox_test( ) function above for a select-multiple selection list were largely but not entirely successful. Again, suppose you want to limit the user to choosing two options from a list of this type. It's fairly simple to get an "Only two selections, please" alert( ) message to pop up if the user tries to select three options; getting that third option to 'deselect' is a much tougher nut to crack, however. Chief problems in this regard:

(1) There are no event handlers, not a one, that are usable with the option element. Sure enough, on my computer the following codings:

<option onEvent="executable_code">Option text</option> and
<span onEvent="executable_code"><option>Option text</option></span>

both fail to execute commands in response to click, mouseover, or other events.

(2) At least the onChange event handler can be used to execute code in response to a change in the 'selection state' of a selection list; however, unlike an onClick action, an onChange action is not cancelable by a return false statement.

After some experimentation, it occurred to me that I could organize the indices of the user's selected options as an array and then use an array element reference to set the selected property of a selected option to false. With respect to the "If you could visit another planet..." script above, then, I renamed the form element and recoded the select element as follows:

<form name="fselect2">
If you could visit two other planets, which ones would they be?<p>
<select name="planet2" size="5" multiple onchange="select_test2( );">

The onChange attribute in the <select> tag triggers the following function:
function select_test2( ) {
var planetcount=0;
var optionIndex = new Array( );
var j=0;
for (i=0; i<document.fselect2.planet2.options.length; i++) {
if (document.fselect2.planet2.options[i].selected) {
optionIndex[j] = i;
j++;
planetcount++; }
if (planetcount==3) {
window.alert("No more than two planets, please.");
document.fselect2.planet2.options[optionIndex[2]].selected=false;
break; } } }

The optionIndex elements are indexed with the variable j, which increments normally (0,1,2...) for each selected option; a separate planetcount variable keeps track of the number of planets chosen by the user. If three options are selected (planetcount=3), then the statement:

document.fselect2.planet2.options[optionIndex[2]].selected=false;

unselects the third-in-source-order of those options*; for example, if the user selects, in order, the 0th (Mercury), 4th (Saturn), and 2nd (Mars) planet options, then the Saturn option (not the Mars option) will be unselected. The effect of the select_test2( ) function thus does not exactly parallel the effect of the checkbox_test( ) function, but it's close.
(*Alternatively, a document.fselect2.planet2.selectedIndex = -1; statement on this line of the script will unselect all three options.)

Try it all out below; for choosing consecutive or nonconsecutive options of a select-multiple selection list, consult if necessary the helpful instructions in the "Lists" section of this page hosted by the EPA (yes, the Environmental Protection Agency).

If you could visit two other planets, which ones would they be?




A refraction of the Primer #29 Script through the prism of regular expressions deserves its own entry, and we'll do that next - we may also bring password field validation into the mix.

reptile7

Comments: Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)