Chapter 5
In This Chapter
Extracting data from drop-down lists
Working with multiple-selection lists
Getting data from check boxes and radio groups
Validating input with regular expressions
Using character, boundary, and repetition operators
Using pattern memory
Getting input from the user is always nice, but sometimes users make mistakes. Whenever you can, you want to make the user's job easier and prevent certain kinds of mistakes.
Fortunately, you can take advantage of several tools designed exactly for that purpose. In this chapter, you discover two main strategies for improving user input: specialized input elements and pattern-matching. Together, these tools can help ensure that the data the user enters is useful and valid.
The most obvious way to ensure that the user enters something valid is to supply him with valid choices. The drop-down list is an obvious and easy way to do this, as you can see from Figure 5-1.
The list-box approach has a lot of advantages over text field input:
If you want to know how to build a list box with the HTML select object, refer to Book I, Chapter 7.
When you're creating a predefined list of choices, create the HTML form first because it defines all the elements you'll need for the function. The code is a standard form:
<body>
<form action = "">
<h1>Please select a color</h1>
<fieldset>
<select id = "selColor">
<option value = "#FFFFFF">White</option>
<option value = "#FF0000">Red</option>
<option value = "#FFCC00">Orange</option>
<option value = "#FFFF00">Yellow</option>
<option value = "#00FF00">Green</option>
<option value = "#0000FF">Blue</option>
<option value = "#663366">Indigo</option>
<option value = "#FF00FF">Violet</option>
</select>
<input type = "button"
value = "change color"
onclick = "changeColor()" />
</fieldset>
</form>
</body>
</html>
The select object's default behavior is to provide a drop-down list. The first element on the list is displayed, but when the user clicks the list, the other options appear.
A select object that the code refers to should have an id field.
The other element in the form is a button. When the user clicks the button, the changeColor() function is triggered.
<select id = "selColor"
onchange = "changeColor()">
The event handler causes the changeColor() function to be triggered as soon as the user changes the select object's value. Typically, you'll forego the user clicking a button only when the select is the only element in the form. If the form includes several elements, processing doesn't usually happen until the user signals she's ready by clicking a button.
Fortunately, standard drop-down lists are quite easy to read. Here's the JavaScript code:
<script type = "text/javascript">
// from dropdownList.html
function changeColor(){
var selColor = document.getElementById("selColor");
var color = selColor.value;
document.body.style.backgroundColor = color;
} // end function
</script>
As you can see, the process for reading the select object is much like working with a text-style field:
You can use the select object in a more powerful way than the method I describe in the preceding section. Figure 5-2 shows a page with a multiple-selection list box.
To make multiple selection work, you have to make a few changes to both the HTML and the JavaScript code.
You modify the select code in two ways to make multiple selections:
The HTML code for multiSelect.html is similar to the dropdownList page, described in the preceding section, but note a couple of changes.
<body>
<h1>Multiple Selections</h1>
<form action = "">
<fieldset>
<label>
Select the language(s) you know.
(ctrl-click to select multiple lines)
</label>
<select id = "selLanguage"
multiple = "multiple"
size = "10">
<option value = "HTML">HTML</option>
<option value = "CSS">CSS</option>
<option value = "JavaScript">JavaScript</option>
<option value = "PHP">PHP</option>
<option value = "MySQL">MySQL</option>
<option value = "Java">Java</option>
<option value = "VB.NET">VB.NET</option>
<option value = "Python">Python</option>
<option value = "Flash">Flash</option>
<option value = "Perl">perl</option>
</select>
<button type = "button"
onclick = "showChoices()">
Submit
</button>
</fieldset>
</form>
<div id = "output">
</div>
</body>
</html>
The code isn't shocking, but it does have some important features:
The JavaScript code for reading a multiple-selection list box is a bit different than the standard selection code described in the section “Reading the list box” earlier in this chapter. The value property usually returns one value, but a multiple-selection list box often returns more than one result.
<script type = "text/javascript">
//from multi-select.html
function showChoices(){
//retrieve data
var selLanguage = document.getElementById("selLanguage");
//set up output string
var result = "<h2>Your Languages</h2>";
result += "<ul>
";
//step through options
for (i = 0; i < selLanguage.length; i++){
//examine current option
currentOption = selLanguage[i];
//print it if it has been selected
if (currentOption.selected == true){
result += " <li>" + currentOption.value + "</li>
";
} // end if
} // end for loop
//finish off the list and print it out
result += "</ul>
";
output = document.getElementById("output");
output.innerHTML = result;
} // end showChoices
</script>
At first, the code seems intimidating, but if you break it down, it's not too tricky.
The standard document.getElementById() technique works fine.
var selLanguage = document.getElementById("selLanguage");
When you're building complex HTML output, working with a string variable is much easier than directly writing code to the element.
var result = "<h2>Your Languages</h2>";
An unordered list is a good way to spit out the results, so I create one in my result variable.
result += "<ul> ";
Use a for loop to examine the list box line by line. Note that selLanguage has a length property like an array.
for (i = 0; i < selLanguage.length; i++){
The currentOption variable holds a reference to each option element in the original select object as the loop progresses.
currentOption = selLanguage[i];
The object currentOption has a selected property that tells you whether the object has been highlighted by the user. selected is a Boolean property, so it's either true or false.
if (currentOption.selected == true){
If the user has highlighted this object, create an entry in the unordered list housed in the result variable.
result += " <li>" + currentOption.value + "</li> ";
After the loop has finished cycling through all the objects, you can close up the unordered list you've been building.
result += "</ul> ";
The output div's innerHTML property is a perfect place to print the unordered list.
output = document.getElementById("output");
output.innerHTML = result;
Check boxes fulfill another useful data input function. They're useful any time you have Boolean data. If some value can be true or false, a check box is a good tool. Figure 5-3 illustrates a page that responds to check boxes.
To build the check box page shown in Figure 5-3, start by looking at the HTML:
<body>
<h1>What do you want on your pizza?</h1>
<form action = "">
<fieldset>
<input type = "checkbox"
id = "chkPepperoni"
value = "pepperoni" />
<label for = "chkPepperoni">Pepperoni</label>
<input type = "checkbox"
id = "chkMushroom"
value = "mushrooms" />
<label for = "chkMushroom">Mushrooms</label>
<input type = "checkbox"
id = "chkSausage"
value = "sausage" />
<label for = "chkSausage">Sausage</label>
<button type = "button"
onclick = "order()">
Order Pizza
</button>
</fieldset>
</form>
<h2>Your order:</h2>
<div id = "output">
</div>
</body>
Each check box is an individual input element. Note that check box values aren't displayed. Instead, a label (or similar text) is usually placed after the check box. A button calls an order() function.
Check boxes don't require a lot of care and feeding. After you extract it, the check box has two critical properties:
The code for the order() function shows how it's done:
//from checkBoxes.html
function order(){
//get variables
var chkPepperoni = document.getElementById("chkPepperoni");
var chkMushroom = document.getElementById("chkMushroom");
var chkSausage = document.getElementById("chkSausage");
var output = document.getElementById("output");
var result = "<ul>
"
if (chkPepperoni.checked){
result += "<li>" + chkPepperoni.value + "</li>
";
} // end if
if (chkMushroom.checked){
result += "<li>" + chkMushroom.value + "</li>
";
} // end if
if (chkSausage.checked){
result += "<li>" + chkSausage.value + "</li>
";
} // end if
result += "</ul>
"
output.innerHTML = result;
} // end function
For each check box,
Use the checked property as a condition.
Radio button groups appear pretty simple, but they're more complex than they seem. Figure 5-4 shows a page using radio button selection.
The most important thing to remember about radio buttons is that, like wildebeests and power-walkers, they must be in groups. Each group of radio buttons has only one button active. The group should be set up so that one button is always active.
You specify the radio button group in the HTML code. Each element of the group can still have a unique id (which comes in handy for associating with a label). Look over the code, and you'll notice something interesting. All the radio buttons have the same name!
<body>
<h1>With what weapon will you fight the dragon?</h1>
<form action = "">
<fieldset>
<input type = "radio"
name = "weapon"
id = "radSpoon"
value = "spoon"
checked = "checked" />
<label for = "radSpoon">Spoon</label>
<input type = "radio"
name = "weapon"
id = "radFlower"
value = "flower" />
<label for = "radFlower">Flower</label>
<input type = "radio"
name = "weapon"
id = "radNoodle"
value = "wet noodle" />
<label for = "radNoodle">Wet Noodle</label>
<button type = "button"
onclick = "fight()">
fight the dragon
</button>
</fieldset>
</form>
<div id = "output">
</div>
</body>
Using a name attribute when everything else has an id seems a little odd, but you do it for a good reason. The name attribute is used to indicate the group of radio buttons. Because all the buttons in this group have the same name, they're related, and only one of them will be selected. Each button can still have a unique ID (and in fact it does). The ID is still useful for associating a label with the button. Once again, this provides a larger click target so the user can click on either the button or the label associated with that button.
The browser recognizes this behavior and automatically unselects the other buttons in the group whenever one is selected.
I added a label to describe what each radio button means.
Getting information from a group of radio buttons requires a slightly different technique than most of the form elements. Unlike the select object, there is no container object that can return a simple value. You also can't just go through every radio button on the page because you may have more than one group. (Imagine a page with a multiple-choice test.)
This issue is where the name attribute comes in. Although ids must be unique, multiple elements on a page can have the same name. If they do, you can treat these elements as an array.
Look over the code to see how it works:
// from radioGroup.html
function fight(){
var weapon = document.getElementsByName("weapon");
for (i = 0; i < weapon.length; i++){
currentWeapon = weapon[i];
if (currentWeapon.checked){
var selectedWeapon = currentWeapon.value;
} // end if
} // end for
var output = document.getElementById("output");
var response = "<h2>You defeated the dragon with a ";
response += selectedWeapon + "</h2>
";
output.innerHTML = response;
} // end function
This code looks much like all the other code in this chapter, but it has a sneaky difference:
Having the right kinds of form elements can be helpful, but things can still go wrong. Sometimes, you have to let the user type things, and that information must be in a particular format. As an example, take a look at Figure 5-5.
A mechanism that checks whether input from a form is in the correct format would be great. This program implements such a feature, checking whether there is content in every field and ensuring the e-mail address and phone number are formatted correctly. You can create this kind of testing feature with string functions, but it can be really messy. Imagine how many if statements and string methods it would take to enforce the following rules on this page:
Although you can enforce these rules, it would be extremely difficult to do so using ordinary string manipulation tools.
JavaScript strings have a match method, which helps find a substring inside a larger string. This tool is good, but we're not simply looking for specific text, but patterns of text. For example, we want to know whether something's an e-mail address (text, an @, more text, a period, and two to four more characters).
Imagine how difficult that code would be to write, and then take a look at the code for the validate.html page:
<script type = "text/javascript">
function validate(){
// get inputs
name = document.getElementById("txtName").value;
email = document.getElementById("txtEmail").value;
phone = document.getElementById("txtPhone").value;
//create an empty error message
errors = "";
//check name - It simply needs to exist
if (name == ""){
errors += "please supply a name
";
} // end if
//check email
emailRE = /^.+@.+..{2,4}$/;
if (email.match(emailRE)){
//console.log("email match");
//do nothing.
} else {
//console.log("email not a match");
errors += "please check email address
";
} // end if
//check phone number
phoneRE = /^(d{3}) *d{3}-d{4}$/;
if (phone.match(phoneRE)){
//console.log("phone matches");
//do nothing
} else {
//console.log("phone problem");
errors += "please check phone #
";
} // end phone if
//check for errors
if (errors == ""){
alert ("now processing data");
//process the form
} else {
alert(errors);
} // end if
} // end function
The code isn't really all that difficult!
Of course, the secret of this program is to decode the mystical expressions used in the match statements. They aren't really strings at all, but very powerful text-manipulation techniques called regular expression parsing. Regular expressions have migrated from the Unix world into many programming languages, including JavaScript.
A regular expression is a powerful mini-language for searching and replacing text patterns. Essentially, what it does is allow you to search for complex patterns and expressions. It's a weird-looking language, but it has a certain charm once you know how to read the arcane-looking expressions.
Table 5-1 summarizes the main operators in JavaScript regular expressions.
Don't memorize this table! I explain in the rest of this chapter exactly how regular expressions work. Keep Table 5-1 handy as a reference.
To see how regular expressions work, take a look at regex.html in Figure 5-6.
The top textbox accepts a regular expression, and the second text field contains text to examine. You can practice the examples in the following sections to see how regular expressions work. They're really quite useful after you get the hang of them. While you walk through the examples, try them out in this tester. (I include it on the website for you, but I don't reproduce the code here. Of course you're always welcome to view the source code.)
The main thing you do with a regular expression is search for text. Say that you work for the bigCorp company, and you ask for employee e-mail addresses. You can make a form that accepts only e-mail addresses with the term bigCorp in them by using the following code:
if (email.match(/bigCorp/)){
alert("match");
} else {
alert("no match");
} // end if
The text in the match() method is enclosed in slashes (/) rather than quote symbols because the expression isn't technically a string; it's a regular expression. The slashes help the interpreter realize this special kind of text requires additional processing.
This match is the simplest type. I'm simply looking for the existence of the needle (bigCorp) in a haystack (the e-mail address stored in email). If bigCorp is found anywhere in the text, the match is true, and I can do what I want (usually process the form on the server). More often, you want to trap for an error and remind the user what needs to be fixed.
You may want to improve the search because what you really want are addresses that end with bigCorp.com. You can put a special character inside the match string to indicate where the end of the line should be:
if (email.match(/bigCorp.com$/)){
alert("match");
} else {
alert("no match");
} // end if
The dollar sign at the end of the match string indicates that this part of the text should occur at the end of the search string, so [email protected] is a match, but not bigCorp.com announces a new Website.
Likewise, you can use the caret character (^) to indicate the beginning of a string.
If you want to ensure that a text field contains only the phrase oogie boogie (and why wouldn't you?), you can tack on the beginning and ending markers. The code /^oogie boogie$/ is a true match only if nothing else appears in the phrase.
In addition to ordinary text, you can use a bunch of special character symbols for more flexible matching:
You can also specify a character class with a range. [a-zA-Z] checks all the letters.
If you want to include an area code with parentheses, just use backslashes to indicate the parentheses: /(ddd) ddd-dddd/. And if you want to ensure the only thing in the sample is the phone number, just add the boundary characters: /^(ddd) ddd dddd$/.
All the character modifiers refer to one particular character at a time, but sometimes you want to deal with several characters at once. Several operators can help you with this process.
The .* combination is especially useful, because you can use it to improve matches like e-mail addresses: /^.*@bigCorp.com$/ does a pretty good job of matching e-mail addresses in a fictional company.
You can also specify a minimum and maximum number of matches, so /[aeiou]{1, 3}/ means “at least one and no more than three vowels.”
Now you can improve the e-mail pattern so that it includes any number of characters, an @ sign, and ends with a period and two to four letters: /^.+@.+..{2,4}$/.
Sometimes you want to remember a piece of your pattern and reuse it. You can use parentheses to group a chunk of the pattern and remember it. For example, /(foo){2}/ doesn't match on foo, but it does on foofoo. It's the entire segment that's repeated twice.
You can also refer to a stored pattern later in the expression. The pattern /^(.).*1$/ matches any word or phrase that begins and ends with the same character. The 1 symbol represents the first pattern in the string; 2 represents the second, and so on.
After you've finished a pattern match, the remembered patterns are still available in special variables. The variable $1 is the first pattern stored; $2 is the second, and so on. You can use this trick to look for HTML tags and report what tag was found: Match ^<(.*)>.*</1>$ and then print $1 to see what the tag was.
There's much more to discover about regular expressions, but this basic overview should give you enough to write some powerful and useful patterns.
HTML5 and CSS3 add a few more tricks to simplify validation, and they are absolutely wonderful.
While you can always use JavaScript and regular expressions to validate your pages (as described in this chapter), HTML5 promises a much easier solution. When you use the special-purpose input elements (described in Book I, Chapter 7), the browser will automatically check the form field to ensure it is in a proper format. If the entry is not valid, the form will (generally) not submit, and the special :invalid CSS pseudo-class will be associated with the invalid field. Simply supply CSS to your page handling the :invalid state:
:invalid {
background-color: red;
}
When this CSS state is active, any invalid fields will have the :invalid styling. For example, if you have an email field defined and the content of that field is not a valid e-mail address, the invalid style will be applied. As soon as the address is in the right format, the invalid style will be removed.
The developer doesn't need to add any other code to the form. Simply add CSS to display invalid entries, and the browser will do the rest. You don't even need to specify the regular expression for e-mail addresses or any other specialty input fields — the appropriate regular expression for each field type is already built in.
Note that if a field is required (with the required attribute), it will be considered invalid until it contains some value.
It is possible that the browser will refuse to process a form until all fields are validated, but this behavior does not yet seem to be universal among HTML5-compliant browsers.
If you wish, you can turn off the validation for any field by adding the novalidate attribute to that element.
Figure 5-7 shows the newElements.html page from Book I, Chapter 7 modified with a nice style sheet and the validation modifiers in place. Note that the name field is required and the e-mail address is invalid, so these fields show the red background I specified for invalid fields.
Please look over the code for html5validation.html on the website — the code hasn't changed substantially from Book I, Chapter 7. The CSS code is new, so I reproduce that here:
<style type = "text/css">
fieldset {
width: 600px;
background-color: #EEEEEE;
margin-left: auto;
margin-right: auto;
box-shadow: 5px 5px 5px gray;
}
label {
float: left;
clear: left;
width: 250px;
text-align: right;
padding-right: 1em;
}
input {
float: left;
}
:required {
border: 1px solid red;
}
:invalid {
color: white;
background-color: red;
}
button {
display: block;
margin-left: auto;
margin-right: auto;
clear: both;
}
</style>
The pattern attribute allows you to specify a regular expression used to validate the form. If the content matches the regular expression, the field will be considered valid. (See the “Working with Regular Expressions” section of this chapter for more details.) The pattern attribute should be used only when the standard validation techniques are not sufficient (that is, you're using an ordinary input element that doesn't have an automatic pattern) because it can be difficult to debug regular expressions.
<input type = "text"
id = "txtPhone"
pattern = "(d{3}) +d{3}-d{4}"
title = "(ddd) ddd-dddd" />
When you specify a pattern, you should also include a title attribute. The title should indicate what the pattern is. The browser can use this as a tip for the user. It may also be useful to add pattern information as placeholder text. (See the placeholder attribute later.)
The required attribute allows you to specify a particular field as required. Supporting browsers will mark all required fields (perhaps by highlighting them in red) if they are not filled in. Some browsers will also send a warning if the user tries to submit a form with empty required fields.
<input type = "text"
required />
The special :required pseudo-class allows you to apply a CSS style to all required elements in your form (giving them a border or background-color, for example). Here's an example of a CSS style for marking required elements with a red border:
:required {
border: 1px solid red;
}
If you have a required field and it has no content, that field will trigger the invalid style.
The placeholder attribute allows you to add a special placeholder value in your text fields. This placeholder acts as a temporary label showing the purpose of the field without requiring a label tag. As soon as the user activates the field, the placeholder text disappears.
<input type = "text"
placeholder = "Name" />
Not all browsers support placeholder text. Other browsers will simply ignore the placeholder attribute. Likewise, if the field is already filled in, the placeholder will not be visible. For these reasons, it is still preferred to add a label so users know what to type in each text area. Placeholder text is especially helpful when it is used to indicate how the input should be formatted (especially if this will be enforced by validation or a pattern).