(Reading: We strongly suggest you read pages 395-398 of JavaScript: A Beginner's Guide with these notes.)
One particularly useful application of fruitful (value-returning) functions is for validating user input. On forms, it's a good idea to verify that the information provided by the user is correct before processing it. For example, we might check that:
Here are two examples of form validation from previous CS110 projects:
Here is the sample form that we will be working with in today's lecture. (View source to see the HTML code.)
As a first example, let's validate that the name field in our form is not blank. First, we need to know how to reference the text field where the user types in her name. Here is the part of the form that defines the name field:
So, the JavaScript variable that holds the contents of the name field is:
document.easyform.yourname.value
To validate the name field, we want to do something like this (the green text is just English description, as opposed to JavaScript):
if (the name text box is blank ) { Show an alert with a reminder message name is not valid } else { name is valid }
| English | JavaScript |
|---|---|
|
the name text box is blank |
|
|
Show an alert with a reminder message |
|
|
Form is done |
|
|
Form is not done yet |
|
Translation:
The most important observation is this: We want
validName() to be a fruitful function that returns a
boolean:
true if the name field is filled out, and
false if it is blank.
In the remainder of these notes, we will use the term boolean function to refer to a fruitful function that returns a boolean value.
Also, the validName() function returns true/false as to
whether the name field is valid. You can easily imagine
similar functions that validate other text inputs, such
as validAddress(), validState, validZip
and so forth. We'll refer to functions that validate one field
as field validators.
Now that we know how to define field validators, how can we use that to validate a form? The default behavior of the SUBMIT button is to send the form's fields to the server when clicked. Its code is simply
The onClick attribute is optional if we want the default
behavior (that is, we want the form's action performed). But
this is not what we want here. We want
onClick = "return true;"
if the yourname field is filled, and
onClick = "return false;"
if the yourname field is empty.
It turns out that if the onClick handler for a submit button
returns false, then its default behavior (the submission of the form)
will not be performed.
(What if no return value is specified for the onClick
handler for a submit button? Empirically, many browsers will still
perform the default action (submission of the form) in this case. So
explicitly returning false seems to be the only way to cancel
the default action.)
So what we really want is something like
onClick="if (validName() == true) {
return true; /* submit the form */
} else {
return false; /* don't submit the form */
}"
Notice that, because validName() returns
true or false exactly when we
want, the preceding code can be simplified to:
onClick = "return validName();"
Therefore, the full code for the SUBMIT button is:
Here is what happens: When the user clicks the button, the
onClick script is executed. This says "return
validName();". Therefore the function validName() is
executed. When it finishes execution, it will return a boolean value,
either true or false. If
validName() returns true, then the
onClick code will be equivalent to
onClick = "return true;"
This will effectively let the submission of the form proceed. If, on
the other hand, validName() returns false, then
the onClick code will be equivalent to
onClick = "return false;"
and the submission of the form will be halted.
Here is the validated form, so you can see it in action.
Here is a page that contains two forms (two copies of the form). Does name validation work on the second form. Why or why not?
Now let's validate the year field of our form. This is more complex than just ensuring the field is non-empty. We want to make sure that it's a valid year. In this context we'll define a valid year as one that is in the range from 1900 to the current year.
The part of the form that defines the year field is
<form name = "easyform" ... > ... <p>Please enter your birth year: <input type = "text" size = "5" name = "birthyear"> ... </form>
so the JavaScript variable that holds the contents of the year field is:
document.easyform.birthyear.value
We can test for a valid year via the following boolean function:
function validBirthYear() {
var year = parseInt(document.easyform.birthyear.value);
var currentYear = (new Date()).getFullYear();
if ((year >= 1900) && (year <= currentYear)) {
return true;
} else {
alert("Please provide a valid birth year\n"
+ "(in the range 1900 -- current year)")
return false;
}
}
If we want our form to validate just the year field, we can simply change
the onClick handler of the SUBMIT button to use
validBirthYear()>:
<input type = "submit"
value = "Click Here to Submit"
onClick = "return validBirthYear();"
>
Here's a version of our form with year validation (but not name validation).
Of course, we don't want a form that validates just one field. We want it to validate multiple fields! For example, we'd like our form to validate both the name and year fields. How can we do that?
Validating multiple fields is easy if we've defined separate boolean functions
for validating the individual field. To validate multiple fields in a form,
we can define a boolean function validForm() that
returns true exactly when all all the fields are
validated and returns false otherwise (i.e., when at least
one field doesn't validate). The validForm() function
simply uses && to combine invocations of the individual
field validation functions.
For example, to validate both the name and year field in our sample form, we define
function validForm() {
var nameOK = validName();
var birthyearOK = validBirthYear();
return nameOK && birthyearOK;
}
and change the onClick handler of the SUBMIT
button to use this new function:
<input type = "submit"
value = "Click Here to Submit"
onClick = "return validForm();"
>
Here's a version of our form that validates both name and year.
The approach described above is a modular
in the sense that
we've broken out the validation of individual fields into these field
validatior
functions. The modular approach to validating forms by
combining individual field validators has many benefits, the most
important of which are:
Note the naming scheme we've adopted: a field fred is
validated by a validFred() function and the result is
stored in a fredOK boolean variable, ready to be combined
with the other boolean variables in one final &&
Warning: Most validators you'll see in textbooks and in the
wild
are not constructed in this modular fashion. Instead,
form validation is often performed by a single large JavaScript function
whose body is a rat's nest of conditionals. But now that you know the
modular approach, you should avoid the bad style you see elsewhere!
Pull-down menus, like the where do you live
menu in our example,
pose a very different validation task. The issue is not checking whether
the user typed any text, but whether they made a choice.
The first thing, of course, is to make sure that you can tell whether they made a choice. For example, it's common to have a menu like the following:
The trouble with this menu is that if the menu shows Science
Center
, you don't know whether the user meant to make that choice,
or forgot to make any choice and the initial value is still there.
So, you need to make sure that the initial choice is invalid.
For example:
It doesn't matter what the text of that initial option is, it just needs to be there. (If you want to make the pre-selected option in the middle of the menu, you can do that, too.)
Now, how can you tell what choice the user made? Each select menu
comes with a property called selectedIndex which tells
you the numerical index (starting at zero because that's how
computer scientists count).
Here's one way of validating that field:
The key with validating text inputs and select menus was to look them up by name. How are the radio buttons named? Consider the following HTML code from our form:
Here is the part of our form that uses radio buttons:
They all have the same name, because logically they belong
to the same group! How do we distinguish which is which? By referring to
them by number. Oh, we have to remember that computer Scientists
start counting at — ahem — zero. So:
document.easyform.graduation[0] is the 2010 button,
document.easyform.graduation[1] is the 2011 button,
document.easyform.graduation[2] is the 2012 button, and
document.easyform.graduation[3] is the 2013 button.
With text inputs, we were interested in the value
attribute, since that's what the user typed. However, with radio
buttons, the values are all part of the form, and we're only interested
in which one was chosen. How can we do that? It turns out that each
radio button input has a checked property which
is true if and only iff that button is currently checked
(chosen). For example, if the user is the class of 2010 and has chosen
that option, the following expression will be true:
Since users can only check one radio button, radio buttons are validated to make sure that something was chosen (checked). Thus, the logic is that at least one has to be checked. Equivalently, complain if none of them has been checked. In other words, if the first one isn't checked, and the second one isn't checked, and. Here is some code that does the trick:
function validGraduation() {
if( the first radio button is checked OR
the second radio button is checked OR
the third radio button is checked OR
the fourth radio button is checked ) {
field is valid
} else {
field is NOT valid
}
}
Now we're ready to translate that logic into JavaScript:
That can, of course, be simplified to the following:
Checkboxes are just like radio buttons, except that you can check more
than one, so validation often takes more complicated guises, such
as choose three options
or choose no more than four
and so
forth, as we saw in our example form:
What were your favorite things about Wellesley? (Choose 1 to 3):
Checking that at least one option was chosen is just like radio button validation, so we'll skip that and go on to something harder, namely counting.
The idea is that if the checked property for an input is
true, we'll add one to a counter. Start the counter at zero, and when the
code is done, we'll know how many were checked. (We've seen code like
this before.)
Notice that we separated the counting of the checkmarks from the
question of whether the field is valid. We could have combined them, but
separating them makes the code clearer, easier to understand, and easier
to re-use. If someone needs to change the validation from 1-3 to 2-4
checkmarks, then only have to look at the
two-line validFavs function, and they can ignore the
eight-line countFavs function.
In the section about form validation
above, we saw how to combine field validators for the name and birthyear
fields to validate both of these fields in a form. We assigned variables
to the boolean values returned by each validator and then used a
big && expression to determine whether they were all
true (valid). We can easily extend this idea to combine all the modular
field validators in any form. In our sample form, for instance, we can
validate all the fields as follows:
What's nice about making each its own function is that you can test them individually, and that's really helpful in narrowing down where the bug is when you have a complex form with many inputs to validate. This is an aspect of modularity, which is one of the big ideas in computer science.
Try it: validated whole form
If we wish to display a message when the form is successfully filled out, we can do that, too:
The more important feedback is to notify the user of what inputs they need to fix in order to submit the form. We could, of course, pop up an alert for each one, but that's not a good idea because alerts are annoying. Far better to mark each one by modifying the style or content of the document, using the JavaScript DOM.
Here, we've surrounded each question with a div with an id, so we can pick out exactly that div. Then, we're going to modify its innerHTML to add a message about it, and we're going to modify the border as well.
See it in action: validated whole form with marking
Is it okay to submit this form?
true or
false from one (not both) of
onClick event handler of the input type = "submit" button
onSubmit event handler of the form tag
true or false value) can be used to validate
individual form fields. It is easy to combine these to validate
multiple fields.
value property
checked property
selectedIndex property
Here is a condensed version of the material from these notes.
Note that beyond here is information that we think you might find interesting and useful, but which you will not be responsible for. It's for the intellectually curious student.
In the section about form validation
above, we used the onClick event handler to execute our form
validation code. However, there is another place we can put form
validation code, namely an onSubmit event handler. When a
form is submitted, an event called submit is generated. This
event is associated with the form itself, not any particular submit
button.
To use it, remove the onClick event handler and write the
same code as the onSubmit event handler for the form:
<form name = "easyform"
method = "post"
action = "http://cs.wellesley.edu/cgi-bin/eform.cgi"
onSubmit = "return validName();" >
NOTE: You only need to invoke validName() once,
either
onClick handler, or
onSubmit handler.
Not in both!
Why might you use an onSubmit event handler instead of
an onClick event handler? One case is that in some forms,
it's convenient to have several submit
buttons. If we want to do
the same form validation in every case, we either copy the same code to
each onClick event handler or, better, put the code in
the onSubmit event handler.
The use of boolean variables in our examples above avoided an
interesting issue, namely the fact that the &&
operator is a short-circuiting operator. (So is
the || operator.) Short-circuiting means that the
evaluation of the expression stops as soon as the answer is known.
Consider the following:
Try running it:
You'll notice that the code stops as soon as you stop following the
instructions. It never asks you another question after that. Why?
Because the various equality tests are being combined
with and (&&),
and and is necessarily false if any of the operands is
false. The short-circuiting is to stop looking at operands as soon as you
find one that is false. (Similarly, the short-circuiting
with or is to stop as soon as you find something that is
true.)
Now, the and and or operators are always short-circuiting in JavaScript. We didn't notice earlier because we has already executed all our field validators, storing the return values in boolean variables. Having done so, the short-circuiting didn't prevent the invocation of any of our field validators.
Suppose, however, that we had written
the validForm function as follows:
With this code, if the name isn't valid, the short-circuiting means
that validYear is never invoked, so the user doesn't find
out whether that part of the form is valid. The validForm
function stops as soon as it finds an invalid field.
The short-circuit behavior of && is desirable when
validation feedback is done with alert boxes, since it would be very
annoying to have an alert box pop up for every invalid field.
But in other forms of validation (in which invalid fields are colored red, for instance), we want all invalid fields to be indicated, not just the first. That's the approach we advocate above, since alerts are so annoying.
Suppose we do want to have non-short-circuit evaluation but we also like the embedded style of programming, without lots of boolean variables?
A second approach is to build our own
non-short-circuit and() function as follows:
// Non-short-circuit and() function
function and (bool1, bool2) {
return bool1 && bool2;
}
Essentially, this ways still uses lots of boolean variables, but it
hides them as the parameters of the and() function.
But the and() function only takes two arguments. What if
we have three, four or even more expressions to combine
with and()? Clearly, we could define a bunch of related
functions, like this:
but that would be yucky. An alternative is to use grouping, just like we do with addition, multiplication and lots of other associative operators:
The number of closing parens on the line with the last prompt is terrifying when you first encounter it, but if you build up the expression, you can see why each exists.
Try running it:
Optional reading: Thau Chapter 11.