In this chapter, we’ll learn how to build forms that use JavaS-cript to validate user input before it’s sent to the server, how to tie together server-side and client-side validation met
Trang 1Here’s the style sheet:
File: risingTooltips.css
ul, div#extra {
display: block;
background-color:blue;
position: absolute;
top: 30px;
left: 0;
width: 100%;
height: 2em;
padding: 0;
margin: 0;
z-index: 20;
}
div#extra {
z-index: 10;
}
li {
display: inline;
font-weight: bold;
padding: 0; margin: 0;
}
li a {
color: white;
background-color: blue;
}
span {
position: absolute;
top: 0;
background: yellow;
border: 1px solid blue;
border-width: 1px 1px 0 1px;
display: none;
}
Finally, here’s the script:
File: risingTooltips.js
var rH = {
addEvent: function(elm, evType, fn, useCapture) {
// addEvent cross-browser event handling for IE5+ NS6/Mozilla Chapter 5: Animation
Trang 2// By Scott Andrew
if (elm.addEventListener) { elm.addEventListener(evType, fn, useCapture);
return true;
} else if (elm.attachEvent) { var r = elm.attachEvent('on' + evType, fn);
return r;
} else { elm['on' + evType] = fn;
} }, init: function() { // get the header links
if (!document.getElementsByTagName ||
!document.getElementById) return;
var navList = document.getElementById('nav');
rH.links = navList.getElementsByTagName('a');
var extra = document.getElementById('extra');
for (var i = 0; i < rH.links.length; i++) { // install event listeners
rH.addEvent(rH.links[i], 'mouseover', rH.mOver, false);
rH.addEvent(rH.links[i], 'mouseout', rH.mOut, false);
// move the corresponding span into the extra div var theLi = rH.links[i].parentNode;
var theSpan = theLi.getElementsByTagName('span')[0];
extra.appendChild(theSpan);
theSpan.style.display = 'block';
// remember where the span is, and what's happening rH.links[i].tipSpan = theSpan;
rH.links[i].tipState = 'none';
} setInterval(rH.moveLinks, 50); // test with 500 },
mOver: function(e) { var link;
if (e && e.target)
Full Rising Tooltips Example Listing
Trang 3link = e.target;
if (window.event && window.event.srcElement) link = window.event.srcElement;
if (!link)
return;
if (link.nodeType == 3) {
link = link.parentNode; // Fix for Safari }
if (link.tipState != 'full') {
link.tipState = 'rising';
}
},
mOut: function(e) {
var link;
if (e && e.target)
link = e.target;
if (window.event && window.event.srcElement) link = window.event.srcElement;
if (!link)
return;
if (link.nodeType == 3) {
link = link.parentNode; // Fix for Safari }
if (link.tipState != 'none') {
link.tipState = 'falling';
}
},
moveLinks: function() {
for (var i = 0; i < rH.links.length; i++) { var link = rH.links[i];
if (link.tipState == 'none' ||
link.tipState == 'full') {
continue;
}
var theSpan = link.tipSpan;
var height = parseInt(theSpan.style.top);
if (isNaN(height)) {
height = 0;
Chapter 5: Animation
Trang 4}
if (link.tipState == 'rising') { height -= 2;
if (height <= -theSpan.offsetHeight) { link.tipState = 'full';
} } else { height += 2;
if (height >= 0) { link.tipState = 'none';
} } theSpan.style.top = height + 'px';
} }, links: []
} rH.addEvent(window, 'load', rH.init, false);
That’s it!
Summary
Animation can be a real enhancement to your sites and Web applications, provided it’s used tastefully It’s possible to use animated GIFs to add a touch
of eye-candy to your pages, but JavaScript’s setTimeout and setInterval functions are a handy tool for even basic animation effects We’ve looked at how
to use these methods, calling them with strings containing JavaScript code or with other functions, and we’ve seen how they can be used in a longer example
of animated tooltips We’ve also explored more advanced function usage in JavaScript, both by specifying anonymous functions and by wrapping a script inside a larger object to avoid it clashing with other included functionality
Summary
Trang 6Forms and Validation
6
Ancient spirits of evil, transform this decayed form … to Mumm-Ra, the Ever Living!
—Mumm-Ra (the Ever Living) Getting user input into your applications through forms is a major part of any Web application or reasonably-sized site That user input, however, needs to be checked to ensure that it’s correct, both to keep your data clean and to avoid security breaches In this chapter, we’ll learn how to build forms that use JavaS-cript to validate user input before it’s sent to the server, how to tie together server-side and client-side validation methods, and learn some DHTML techniques
to improve the usability or convenience of your form pages
Ultimately, the information that’s submitted to your Web server is entirely under the control of the end user, no matter how many client-side safeguards you put
in place Any improvement in the user experience must always rest atop a secure foundation on the server Client-side validation can only ever be an enhancement
to an already secure system Your server-side code must always check the user’s input, no matter how sophisticated the page’s client-side processing is
With that dire warning out of the way, let’s see how DHTML can bring benefits
to forms
Trang 7Reasons for Form Validation
The whole purpose of computer-based data management systems is to store user data more reliably than a paper-based system That’s why HTML forms exist HTML forms alone are not enough, however Generally speaking, form elements need to be wrapped in extra processing Here are some basic reasons why form validation is a good idea
Storing Clean Data
When the back end of your Web application receives user input through a form, it’s vital to check that the data arrives in a proper format, and reject it if it does not For example, if you need to capture an email address from the user, you need
to check that the entered address matches the format: someone@somewhere.something Addresses entered incorrectly, whether through mistyping on the part of the user, or as a deliberate attempt to hide the address, will pollute your database and are not worth capturing.1 A polluted or corrupt database is a data administration nightmare, and can ruin the performance of reports, Web pages, screens and other applications that exist miles away from your own code You don’t want that
Defending Against Security Exploits
Unknown and unchecked data can cause security breaches when its processed
on the server There are many well-publicized attacks on Websites that involve
techniques such as SQL injection and cross-site scripting.2 You can’t resist all security attacks just by validating incoming data, but making sure that submitted data matches expected formats is a big step in the right direction
In the trivial case, in which data is not submitted to a complex interpreted system like a database, simple formatting checks might suffice for validation For example,
a phone number shouldn’t ever contain a left-angle-bracket or an apostrophe Usually, though, when data is submitted to a database (or to any interpreted language, such as SQL, PHP, Perl, or Python), you should make use of any features
1 Note that, if you’re getting a lot of invalid data, it’s important to think about why that’s happening.
If many users don’t want to supply an email address, maybe that field should be optional rather than compulsory.
2
Descriptions of vulnerabilities and the methods that you can use to avoid them are beyond the scope of this book: the whitepapers at http://www.spidynamics.com/support/whitepapers/ provide a useful grounding Web application developers must be aware of these problems.
Chapter 6: Forms and Validation
Trang 8that language makes available to safely handle unexpected input (e.g character escaping) Again, these procedures must be handled on the server, as any measures that utilize JavaScript on the client side may be disabled with little effort on the part of an attacker
Improving User Interactivity
Finally, form validation can improve the user’s data entry experience If some of the user’s input errors can be caught using JavaScript validation on the client-side, then the need for a round trip to the server is avoided, and the user receives feedback faster That’s good for the user’s workflow, and good for reducing server load If the client-side validation includes useful visual hints, then the user’s life can be made easier again With the right hints in place, the user will be led helpfully through the form and will make fewer data entry mistakes in the first place
Simple Client-Side Validation
Let’s look at the building blocks we’ll use to implement DHTML form validation These two object signatures should give you a taste of where we’re headed:
var validationSet = { 'field1': { … }, 'field2': { … }, …
};
var fV = { addEvent: function(elm, evType, fn, useCapture) { … }, init: function() { … },
checkValidSubmit: function(e) { … }, checkSubmit: function() { … }, checkValid: function(e) { … }, handleValidity: function(t) { … } }
The first of these objects will hold validation data for a specific page The second object is a library object that holds all the DHTML processing code It’s always the same, no matter what form fields are on the page
Improving User Interactivity
Trang 9Using Regular Expressions
The simplest way to express validation requirements such as “this phone number field can only contain digits, parentheses, spaces, and hyphens” is to use regular expressions Although they’re sometimes difficult to compose, regular expressions are generally a better choice that trying to construct validation code that analyses submissions with string operations The problem with string analysis is that every case requires different logic, whereas with regular expressions, at least you know that there will be exactly one per form field That’s a bit more, well, regular!
A regular expression that matched our phone number requirement above might be:
^[- ()0-9]+$
This regular expression makes the field compulsory: the [- ()0-9] section means
“match any single character that’s a hyphen, a parenthesis, a space, or a digit.” The trailing + means “match the longest available string consisting of one or more
of the preceding characters.” Finally, the two anchors ^ (match the start of the string) and $ (match the end of the string) ensure that the whole typed-in value—not just some part of it—must match Put together, these restrictions mean that not only is a phone number required to match this regular expression, but an empty string will not match it: the field is compulsory If the field was optional, we could use this alternate regular expression:
^[- ()0-9]*$
Here, the * means “match zero or more of the preceding characters.” Since an
empty-string is indeed zero or more characters, it will match, so the field can be left empty in this case
We can apply validation checks to fields by specifying a regular expression for each field we wish to validate The contents of the field must match the regular expression, or we will refuse to submit the form Note, however, that a simple way around this is to turn JavaScript off in the Web browser and reload the page Again: this solution is good for usability, not security
Regular expressions are powerful, but represent quite a complex subject Fortu-nately, there are a lot of resources designed to help the newcomer SitePoint’s own guide3 is a good primer
3 http://www.sitepoint.com/article/expressions-javascript
Chapter 6: Forms and Validation
Trang 10You can never be too careful with regular expressions The expression we saw above allows these (correct) phone numbers:
(03) 9415 5200 911
(916) 657-9900 However, it also allows this messy possibility:
00034 5( (1)(4 2-2-(2(
Clearly, in a real application, you need to do your best to craft a regular expression that’s bulletproof The simple one we picked earlier is suitable for this discussion, though
Connecting Regular Expressions to Fields
The best time to check whether a field’s contents are valid is when the user moves
away from the field, either by pressing Tab, by hitting Enter, or by clicking
elsewhere in the document Sometimes, you might validate a second time just before the form is submitted This is also a good point at which to check that any dependencies between fields are correct Finally, if you want, you can validate
on every letter that’s typed; such measures are usually used only for special effects, since it’s harder to provide non-disruptive feedback It’s best to wait until each field has been exited before you perform your checks
Here’s how you can do just that Each form element fires a blur event when the user moves away from it, so that’s where we should attach an event listener That listener will examine the content of the field and warn the user if it’s not valid
We will also need a set of regular expressions—one for each field that needs val-idating—against which to check the field contents The easiest way to maintain this set will be to record the regular expression against the name of the matching field On loading the page, we’ll walk through the set of field names and regular expressions and attach one event listener to each element named in the list
An example may clarify this slightly: imagine that we have a page with two text elements, one with the name phone, for entry of a phone number, and one with the name email, for entry of an email address
JavaScript has a variable type that’s ideal for storing a set of named items: the Object type We saw in Chapter 5 how an object literal can be used to store a
set of methods It’s just as easy to store plain data In this case, we’ll use nested
Connecting Regular Expressions to Fields
Trang 11literal objects (objects inside objects) We do that because we might (eventually)
want to store more than one piece of information against each form field So, each field name will have its own object Here’s the result:
var validationSet = {
'phone': {
'regexp': /^[- ()0-9]+$/
},
'email': {
'regexp': /^.+?@.+?\ +?$/
}
};
Notice that the object property names are strings ( 'foo'), rather than variable names (foo) JavaScript allows this, provided you’re careful when accessing the properties Effectively, the result is a set of fields indexed by strings In other languages, this type of set (which associates a key, in this case a string like 'phone', with a value like '^[- ()0-9]+$') is variously called a dictionary, a hash, an associative array, or a map One difference between JavaScript and
such other languages is that, in JavaScript, all these things are one: an object Only JavaScript arrays (which we’re not using here) have the extra feature of a length property that makes them stand slightly apart from other objects like the one we’re using here
Another new piece of syntactic sugar in this example is the use of slashes (/…/)
to delimit regular expressions, thereby distinguishing them from normal strings, which use quotes
When the page loads, we can then iterate through the set, look up the fields that have names recorded in the set (phone and email), and add a single listener, checkValid, to each one:
File: genericValidation.js (excerpt)
for (var i in validationSet) {
if (document.getElementsByName(i)) {
var formField = document.getElementsByName(i)[0];
fV.addEvent(formField, 'blur', fV.checkValid, false); }
}
The idiom for (var i in validationSet) iterates through each key (property name) in a dictionary (a JavaScript object), and is very useful when using diction-aries to hold data For each key in the dictionary, we then check that there is an Chapter 6: Forms and Validation