

PHP:
/* get the action */
$action = @$_GET['action'];
if (isset($_GET['id'])) {
/* the user has already made a request and been given an email ID */
try {
$email = new EmailValidator((int)$_GET['id']);
if (isset($_GET['email'])) {
/* a new email has been sent, so we must re-validate */
$email->setEmail($_GET['email']);
$action = 'validate';
}
} catch (EmailValidatorException $e) {
if (isset($_GET['email'])) {
/* the ID was invalid, but we have been sent an email address */
$email = new EmailValidator($_GET['email']);
$action = 'validate';
} else {
/* an invalid ID was specified and no email address was given - we cannot do much */
$action = 'invalid';
}
} catch (Exception $e) {
$action = 'unknownerror';
}
} else if (isset($_GET['email'])) {
try {
/* load the new email into the validator */
$email = new EmailValidator($_GET['email']);
} catch (Exception $e) {
$action = 'unknownerror';
}
} else {
/* no action sent – we cannot do much */
$action = 'invalid';
}
Processing the Data
We use a switch statement on the action variable to carry out the required action and get a true or false response. This is stored in a variable called $ret which we return to the Ajax application to indicate success or failure.
PHP:
$data = '';
switch ($action) {
case 'validate':
$ret = validate_email($email);
$data = (string) $email->getId();
break;
case 'verify':
try {
$ret = $email->verify(@$_GET['v_code']);
} catch (EmailValidatorException $e) {
/* the email has not been validated - validate it now */
$action = 'validate';
$ret = validate_email($email);
}
$data = (string) $email->getId();
break;
case 'unknownerror':
$ret = 0;
break;
default:
//fail
$action = 'invalid';
$ret = 0;
}
function validate_email(EmailValidator $email)
{
/* validate the email */
if(! $email->validate()) {
return false;
}
$email->save(); // save it to the database
$email->sendVerificationEmail(); // send the verification email
return true;
}
Again, notice how we modify the action variable and instead validate the email address if verification fails. If the email turns out to be valid, the sendVerificationEmail method is called. This causes the EmailValidator to generate a random 5 character verification code and send it in an email to the supplied email address.
Returning a Response
The response sent back to the Ajax engine is not like a normal response which is sent as HTML. We return the response as plain text in the form of a semi-colon delimited string containing three fields:
status;data;context
* status – set to 1 if the request was successful and 0 if the request was unsuccessful
* data – this contains the email ID generated by the EmailValidator class
* context – this is set to the value of the action variable. It is usually the same as value passed by the Ajax engine but may change if the email has been revalidated or an error occurred. The context is used by the Ajax engine to decide how to respond.
We generate the response by joining the $ret, $data and $action variables together with semi-colons.
PHP:
/* send the response as plain text */
header('Content-Type: text/plain');
echo((int) $ret . ';' . $data . ';' . $action);
The HTML Code
The HTML code itself contains only a link to the script containing the Ajax engine. This ensures that the script is only downloaded if the browser supports Javascript. The HTML code itself produces a fully functioning HTML form which does not require Ajax. If Ajax is not enabled the script receiving the form data will process and validate the email address normally.
HTML:
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Ajax Email Validation</title>
<script src="ajax_validation_engine.js" type="text/javascript"></script>
</head>
<body onload="init();">
<h1>AJAX Email Validation</h1>
<p>The form below demonstrates how to use AJAX
to validate an email while the user is filling out a form. Fill in
the form below for a demo:</p>
<div>
<form id="formEmail" action="input.php" method="post">
<div>
<div id="emailMsg" style="display:none"></div>
<label for="txtEmail">Email Address: </label>
<input type="text" id="txtEmail" name="email" />
<span id="verify" style="display: none"><br />
<span id="verifyMsg"></span><br />
<label for="txtVerify">Verification Code: </label>
<input id="txtVerify" type="text" name="v_code" />
<input type="hidden" name="email_id" value="" />
<button id="btnVerify" onclick="verifyAddress(); return false;">Verify</button>
</span>
</div>
<div>
<label for="txtFirstName">First Name: </label>
<input type="text" id="txtFirstName" name="fname" />
</div>
<div>
<label for="txtLastName">Last Name: </label>
<input id="txtLastName" type="text" name="sname" />
</div>
<p>A Little about yourself:<br /><textarea cols="20" rows="10" name="bio"></textarea></p>
<p><input id="btnSubmit" type="submit" /></p>
</form>
</div>
</body>
</html>
The Ajax Engine
The Ajax engine responds to events in the user interface and initializes communication with the server-side script. It also responds to the return data from the server-side script and takes the appropriate action based on the response.
Creating an instance of the XMLHTTP Object
The method used to create an instance of the XMLHTTP object depends on the browser being used. While Firefox and Netscape expose the XMLHttpRequest object, Internet Explorer exposes several different versions of the MSXML ActiveX control that depends on the version of Internet Explorer that is being used. The getXMLHTTP function determines which method is supported (if any) and creates an instance of the appropriate object. If it fails, it returns false.
Javascript:
function getXMLHTTP()
{
if ((typeof XMLHttpRequest) != "undefined") {
/* XMLHTTPRequest present, use that */
return new XMLHttpRequest();
} else if (window.ActiveXObject) {
/* there are several versions of IE's Active X control, use the most recent one available */
var xmlVersions = ["MSXML2.XMLHttp.5.0",
"MSXML2.XMLHttp.4.0",
"MSXML2.XMLHttp.3.0",
"MSXML2.XMLHttp",
"Microsoft.XMLHTTP"];
for (var x = 0; x < xmlVersions.length; x++) {
try {
var xmlHTTP = new ActiveXObject(xmlVersions[x]);
return xmlHTTP;
} catch (e) {
//continue looping
}
}
}
/* if none of that worked, return false, to indicate failure */
return false;
}
Initializing the Environment
The HTML page which contains the form loads originally without any Javascript. This ensures that it is fully functional without Ajax. The init function initializes the Ajax environment by creating an instance of the XMLHTTP object and setting up all of the global variables. Most importantly, if we cannot create an XMLHTTP object, we exit the function and allow the user to continue filling out the form without Ajax. This fall back feature is very important, as the likely result of telling a user that they cannot signup to your site until they enable Javascript will be the user going elsewhere (and not returning).
Javascript:
var xmlHTTP;
var theForm;
var btnSubmit;
var txtEmail;
var verify;
var verifyMsg;
var txtVerify;
var btnVerify;
var emailMsg;
var emailValidate;
function init()
{
/* close the document output*/
document.close();
xmlHTTP = getXMLHTTP();
if (! xmlHTTP) {
/* do not continue if XML HTTP is not available */
return;
}
/* initialize global variables */
theForm = document.getElementById('formEmail');
btnSubmit = document.getElementById('btnSubmit');
txtEmail = document.getElementById('txtEmail')
verify = document.getElementById('verify')
verifyMsg = document.getElementById('verifyMsg');
txtVerify = document.getElementById('txtVerify');
btnVerify = document.getElementById('btnVerify');
emailMsg = document.getElementById('emailMsg');
emailValidate = 'email_validate.php?action=';
/* disable the submit button */
btnSubmit.setAttribute('disabled', 'disabled');
txtEmail = document.getElementById('txtEmail');
/* set the onchange event of the email input box to the validateAddress() function */
txtEmail.onchange = validateAddress;
}
Sending a Request
To send an HTTP request using the XMLHTTP object, we need to first call the open function with three parameters:
XmlHTTP.open(method, uri, async);
* method – this should be either 'get' or 'post'. For a get request, data to be passed to the script is appended to the URI using a query string in the form: uri.php?var1=value1&var2=value2. For a post request, the data is passed to the script in the HTTP request body. We use the uriEncodeComponent function to url encode the data to be sent.
* uri – the path to the receiving script, relative to the current page (for reasons of security the open method will only open a document within the same domain as the current document).
* async – tells us whether the request should be asynchronous. When set to true, the request for the document is sent and script execution continues immediately. If set to false, the script waits until the response has been fully received. It is advisable in most cases to set this to true, as a slow server could cause the user interface to hang.
To send the request, we need to call the send function. Once a response is received, the responseText property will contain the text sent by the server side script in the HTTP response body. The location at which this property is available is dependent on whether the request was synchronous or asynchronous. With a synchronous request, the script waits for the response before continuing, therefore, the responseText property is available immediately after the the call to send. In contrast, the script does not wait for the response if the request was asynchronous. Instead, the XMLHTTP object fires the onreadystatechange event. Each time the event is fired, the readyState property of the XMLHTTP has changed. A readyState value of 4 signifies that the response has been fully received and the responseText property contains the HTTP response body.
Synchronous Request
xmlHTTP.open('get', uri, false);
xmlHTTP.send(null);
alert(xmlHTTP.responseText);
Asynchronous Request
xmlHTTP.open('get', uri, true);
xmlHTTP.onreadystatechange = function () {
if(xmlHTTP.readyState == 4) {
alert(xmlHTTP.responseText);
}
};
xmlHTTP.send(null);
A value of null is always supplied as the first argument to the send function in the case of a get request. In a post request, the data is sent in the body of the request--this is done by passing a url-encoded string to the send function instead of null.
Parsing the Response
In our email validation application, we will be using asynchronous requests and assigning a generic function to the onreadystatechange event. This function will parse the response, validate it and use the context supplied by the validation script to call the appropriate function to deal with the response.
Javascript:
function parseResponse()
{
if (xmlHTTP.readyState != 4) {
/* the request is not complete */
return;
}
var responseArray = xmlHTTP.responseText.split(';');
var response = Array();
if (responseArray.length != 3) {
/* response is invalid */
return;
}
/* load the response into a logical associative array */
response['status'] = parseInt(responseArray[0]);
response['data'] = responseArray[1];
response['context'] = responseArray[2];
/* decide what to do based on the response context */
switch(response['context']) {
case 'validate':
validateCallback(response);
break;
case 'verify':
verifyCallback(response);
break;
case 'invalid':
// return everything to its original state and start over
startupState();
validateAddress();
break;
case 'unknownerror':
// something is wrong - so we are going to fall back
btnSubmit.removeAttribute('disabled');
txtEmail.onchange = null;
verify.style.display = 'none';
break;
}
}
This function will be called each time the readstatechange event is fired. Note that the Ajax part of the application is disabled if an unknown error occurs. This ensures that the user can still submit the form if email validation is not functioning. It is important to plan for failure in any application, but more so in Ajax applications. Users have become accustomed to submitting forms and presenting them with an unsubmittable form is likely to cause frustration as the user may not realize that any communication has been made with the server.
Validating the Email Address
Validating the email address is done in two parts. The validateAddress function is assigned to the onchange event handler of the email input box. This event is fired each time a change is made to the contents by the user. The validateAddress function performs the HTTP request and sends the appropriate information such as the email address and the ID in the query string.
Javascript:
function validateAddress()
{
var email = theForm.email.value;
var id = parseInt(theForm.email_id.value);
startupState();
if (email != '') {
/* add the email address to the query string */
var dataString = 'email=' + encodeURIComponent(email);
if (id > 0) {
/* if an ID is set, add that too */
dataString += '&id=' + id;
}
} else {
/* no email entered, do nothing */
return;
}
/* send the request */
xmlHTTP.open('get', emailValidate + 'validate&' + dataString, true);
xmlHTTP.onreadystatechange = parseResponse;
xmlHTTP.send(null);
}
function startupState()
{
verify.style.display = 'none';
verifyMsg.innerHTML = '';
txtVerify.removeAttribute('disabled');
btnVerify.removeAttribute('disabled');
btnVerify.innerHTML = 'Verify';
btnSubmit.setAttribute('disabled', 'disabled');
emailMsg.innerHTML = '';
theForm.email_id.value = '';
theForm.v_code.value = '';
}
I mentioned earlier that we are using asynchronous requests. Therefore the second part of the email validation takes place when the response has been received from the server. The validateCallback function is called when the server responds with a 'validate' context. If validation was successful, the verification box is displayed.
Javascript:
function validateCallback(response)
{
if (response['status']) {
/* the email address is valid and a verification email has been sent */
emailMsg.style.display = 'none';
verify.style.display = 'inline';
verifyMsg.innerHTML = '<b>An Email containing your verification code has been sent to this address. Please Enter it before continuing.</b>';
theForm.email_id.value = response['data'];
} else {
/* the email address was invalid */
verify.style.display = 'none';
emailMsg.innerHTML = '<b>You have entered an invalid email address. Please correct it before continuing.</b>';
emailMsg.style.display = 'block';
theForm.email.focus();
}
}
Verification follows a similar pattern to validation. There is verifyAddress function which is executed when the Verify button is clicked and the verifiyCallback function which is executed when the server responds with a context of 'verify'.
Javascript:
function verifyAddress()
{
var v_code = theForm.v_code.value;
var id = theForm.email_id.value;
if (v_code == '') {
/* no verification code has been entered */
alert('No Verification Code Entered');
theForm.v_code.focus();
return;
}
/* add the verification code to the query string */
var dataString = 'id=' + encodeURIComponent(id) + '&v_code=' + encodeURIComponent(v_code);
xmlHTTP.open('get', emailValidate + 'verify&' + dataString, true);
xmlHTTP.onreadystatechange = parseResponse;
xmlHTTP.send(null);
}
The verifyCallback function checks for successful verification. If verification succeeded, the verify button's text is changed to “verified”, disabled and the submit button is enabled. It also clears the email input box. This means that when the form is submitted, only the email ID is passed to the receiving script and not the email address. The email ID can then be used by the script which processes the form data to check the address has been verified--if it has, it need not be verified again.
Javascript:
function verifyCallback(response)
{
var v_code = theForm.v_code.value;
theForm.email_id.value = response['data'];
if(response['status']) { // successful verification
btnVerify.setAttribute('disabled', 'disabled');
txtVerify.setAttribute('disabled', 'disabled');
btnVerify.innerHTML = '<i>Verified</i>';
verifyMsg.innerHTML = '';
/* enable the submit button */
btnSubmit.removeAttribute('disabled');
verifyMsg.innerHTML = '<b><i>' + theForm.email.value + '</i></b>';
theForm.email.value = '';
} else {
verifyMsg.innerHTML = '<b>Verification Failed</b>';
}
}
Putting it All Together
Congratulations, you now have a working Ajax application which validates emails. You should have the following four files in the same directory:
email_validate.php
email_validator.php
ajax_validation_engine.js
ajax_form.html
Ajax Email Validation
The three files required for this application are also included in the ZIP file.
Extending this Example
To keep the application simple, we have omitted some details which would be advisable to implement in a real world scenario:
* An extra box field should be included in the form to confirm the email address. Only when both email addresses match, should the Ajax engine send it to the server and validate.
* The email EmailValidator includes a remove method, which should be used by the script receiving the form data to remove the email address from the database once verification has been confirmed. This will prevent the database from growing to an unsavory size.
* We have not created the input.php script which receives the form data. If the email address was validated, it will not be sent to the input.php script--instead the email_id variable will be sent. The script can then use the email validator to check whether or not the email has been verified.
* By default, the EmailValidator creates the SQLite database in the same directory as the running script. For obvious security reasons, you should change the path of the database to one which is inaccessible by the web server.
Sites Which use Ajax
Below is a small taste of the many web sites with applications which use Ajax. This list is likely only to grow. So be one of the first web developers to jump on the Ajax bandwagon!
* Yahoo News - Hovering over the links to articles produces a small image and synopsis or the news item.
* Google Suggest - The text typed in the search box is buffered and sent to the Google server at regular intervals, producing a dropdown list of suggested searches.
* A9 - Amazon-based search engine which, in conjunction with XMLHTTP and IFRAMES, delivers an interfaces which responds instantly to user input, updating search results immediately.
* Google Mail - The Gmail application again uses a combination of IFRAMES and XMLHTTP to create a sleek responsive interface. Google also uses a pattern known as polling to regularly check the user's email.