At the Forge - Beginning Ajax
Listing 1. ajax-test.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">
<head><title>Ajax test</title>
<script type="text/javascript">
function getXMLHttpRequest () {
try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) {};
try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) {}
try { return new XMLHttpRequest(); } catch(e) {};
return null;
}
function parseHttpResponse() {
alert("entered parseHttpResponse");
if (xhr.readyState == 4) {
alert("readystate == 4");
if (xhr.status == 200) {
alert(xhr.responseText);
}
else
{
alert("xhr.status == " + xhr.status);
}
}
}
var xhr = getXMLHttpRequest();
alert("xhr = " + xhr);
xhr.open("GET", "atf.html", true);
xhr.onreadystatechange = parseHttpResponse;
xhr.send(null);
</script>
</head>
<body>
<h2>Headline</h2>
<p>Paragraph</p>
</body>
</html>
Note that ajax-test.html, although simple, is a fully working Ajax program. In order for it to work, you need to have a file named atf.html in the DocumentRoot directory of your Web site. (Otherwise, you will get an HTTP response code of 404.) If you've ever wondered how hard it is to perform an Ajax call, you now can see that it's relatively simple.
Now that we have seen how an Ajax program works, let's use this knowledge to modify the registration program that we built last month. Our old registration page defined a list of user names in the JavaScript. If the user's requested user name was a member of that list, we alert the user to the error and forbid the user from actually registering.
I won't describe all of the problems with this approach, as there are many. As a simple alternative, what if we were to use Ajax to retrieve the list of user names? That way, we could be sure that the list was up to date.
What if, instead of having the array contents hard-coded, we were to download them from a Web page on the server? (This is admittedly not as sophisticated as getting a yes or no answer to a specific user name; we will get to that functionality in next month's column.) If the Ajax-retrieved list of user names was generated dynamically, we could have it grab appropriate data from the database and then return an XML document that easily could be turned into an array. To make the example easier in this month's column, we don't use a dynamic page, but rather a static one. However, if you have done any server-side Web programming in the past, you probably will understand how to take our file, usernames.txt (Listing 2), and turn it into a dynamic page.
Listing 2. usernames.txt
abc def ghi jkl mno pqr stu vwx yzz
A registration page that follows this principle is shown in Listing 3. That file, ajax-register.html, is similar to the registration form we created last month. In last month's non-Ajax version, we defined an array (usernames). We then defined a checkUsername function that is invoked by the onchange handler for the username text field. This had the effect of invoking checkUsername when the user completed the user name. If the requested user name was in the usernames array, the user was given a warning, and the submit button was disabled. Otherwise, the user was able to submit the form to the server-side registration program, presumably as a first step to participating in the site.
Listing 3. ajax-register.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">
<head><title>Register</title>
<script type="text/javascript">
function getXMLHttpRequest () {
try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) {};
try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) {};
try { return new XMLHttpRequest(); } catch(e) {};
return null;
}
function removeText(node) {
if (node != null)
{
if (node.childNodes)
{
for (var i=0 ; i < node.childNodes.length ; i++)
{
var oldTextNode = node.childNodes[i];
if (oldTextNode.nodeValue != null)
{
node.removeChild(oldTextNode);
}
}
}
}
}
function appendText(node, text) {
var newTextNode = document.createTextNode(text);
node.appendChild(newTextNode);
}
function setText(node, text) {
removeText(node);
appendText(node, text);
}
var xhr = getXMLHttpRequest();
function parseUsernames() {
// Set up empty array of usernames
var usernames = [ ];
// Wait for the HTTP response
if (xhr.readyState == 4) {
if (xhr.status == 200) {
usernames = xhr.responseText.split("\n");
}
else
{
alert("problem: xhr.status = " + xhr.status);
}
}
// Get the username that the person wants
var new_username = document.forms[0].username.value;
var found = false;
var warning = document.getElementById("warning");
var submit_button = document.getElementById("submit-button");
// Is this new username already taken? Iterate over
// the list of usernames to be sure.
for (i=0 ; i<usernames.length; i++)
{
if (usernames[i] == new_username)
{
found = true;
}
}
// If we find the username, issue a warning and stop
// the user from submitting the form.
if (found)
{
setText(warning, "Warning: username '" + new_username
+"' was taken!");
submit_button.disabled = true;
}
else
{
removeText(warning);
submit_button.disabled = false;
}
}
function checkUsername() {
// Send the HTTP request
xhr.open("GET", "usernames.txt", true);
xhr.onreadystatechange = parseUsernames;
xhr.send(null);
}
</script>
</head>
<body>
<h2>Register</h2>
<p id="warning"></p>
<form action="/cgi-bin/register.pl" method="post">
<p>Username: <input type="text" name="username"
onchange="checkUsername()" /></p>
<p>Password: <input type="password" name="password" /></p>
<p>E-mail address: <input type="text" name="email_address" /></p>
<p><input type="submit" value="Register" id="submit-button"/></p>
</form>
</body>
</html>
To turn last month's registration page into an Ajax-style one, we modify the checkUsername function, which is invoked when the user finishes entering his or her requested user name. Instead of defining the usernames array, we instead have checkUsername fire off an Ajax request to the server. Unlike last month's non-Ajax version, this is all that checkUsername will do. The updated function looks like this:
function checkUsername() {
xhr.open("GET", "usernames.txt", true);
xhr.onreadystatechange = parseUsernames;
xhr.send(null);
}
As you can see, our function is requesting the file usernames.txt from the server. When xhr's state changes, we ask to invoke the parseUsernames function. It is in this function that we have put the serious logic, first turning the retrieved file contents into an array:
var usernames = [ ];
if (xhr.readyState == 4) {
if (xhr.status == 200) {
usernames = xhr.responseText.split("\n");
}
}
Here, we see the standard Ajax pattern repeated from the previous example: wait for xhr.readyState to be 4, and then check that xhr.status (the HTTP response status code) is 200. At that point, we know we have received the contents of usernames.txt, which (as you can see from Listing 2) contains the existing user names, one user name per line. We use JavaScript's split function to turn this into an array, which we assign to usernames.
From this point on, we can reuse the logic from last month's non-Ajax version, first grabbing the various node IDs from the page, using DOM methods:
var new_username = document.forms[0].username.value;
var found = false;
var warning = document.getElementById("warning");
var submit_button = document.getElementById("submit-button");
Then, we check to see if the requested user name is in our array:
for (i=0 ; i<usernames.length; i++)
{
if (usernames[i] == new_username)
{
found = true;
}
}
If the user name is found in the list, we issue a warning at the top of the page. Otherwise, we clear out any warning that might be there:
if (found)
{
setText(warning, "Warning: username '" + new_username +"' was taken!");
submit_button.disabled = true;
}
else
{
removeText(warning);
submit_button.disabled = false;
}
}
Now, is this a good way to handle the checking of user names? Not really—although now that we have the basic Ajax logic in place, we can modify it slightly to be more efficient and secure.
One problem is that the list of user names is in a static file. Perhaps our server is running a cron job that creates usernames.txt on a regular basis, but that seems a bit silly when we can instead use a server-side program to query the database dynamically. Switching from a static file to a dynamic page thus seems like a good idea, if only for performance reasons.
There are security reasons as well. As with last month's version, we are downloading the entire list of user names to the user's browser. This means that a potentially malicious user would have access to all of the user names and would be able to poke through them, either with the intention of trying to break into the site or spam the users.
One potential downside of using Ajax for this type of check is the speed issue. As I indicated previously, the core of Ajax is its asynchronous nature, which means that we cannot know how long it will take for the server to respond to our query. In my simple tests, the round trip from my browser to my server and back was nearly instantaneous, and it provided me with useful feedback right away. On a more heavily loaded server, or with a more sophisticated database query, or if users have slow Internet connections, asynchronous calls might begin to feel sluggish. That said, even the worst Ajax function will likely be faster than a page refresh, because of the reduced overhead that is involved.
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.
Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.
Sponsored by ActiveState
| Containers—Not Virtual Machines—Are the Future Cloud | Jun 17, 2013 |
| Lock-Free Multi-Producer Multi-Consumer Queue on Ring Buffer | Jun 12, 2013 |
| Weechat, Irssi's Little Brother | Jun 11, 2013 |
| One Tail Just Isn't Enough | Jun 07, 2013 |
| Introduction to MapReduce with Hadoop on Linux | Jun 05, 2013 |
| Android's Limits | Jun 04, 2013 |
- Containers—Not Virtual Machines—Are the Future Cloud
- Lock-Free Multi-Producer Multi-Consumer Queue on Ring Buffer
- Linux Systems Administrator
- Introduction to MapReduce with Hadoop on Linux
- Senior Perl Developer
- Technical Support Rep
- Weechat, Irssi's Little Brother
- UX Designer
- One Tail Just Isn't Enough
- Android's Limits
Featured Jobs
| Linux Systems Administrator | Houston and Austin, Texas | Host Gator |
| Senior Perl Developer | Austin, Texas | Host Gator |
| Technical Support Rep | Houston and Austin, Texas | Host Gator |
| UX Designer | Austin, Texas | Host Gator |
| Web & UI Developer (JavaScript & j Query) | Austin, Texas | Host Gator |
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?




8 min 24 sec ago
8 min 51 sec ago
2 hours 33 min ago
6 hours 44 min ago
6 hours 47 min ago
1 day 2 hours ago
1 day 3 hours ago
1 day 3 hours ago
1 day 6 hours ago
1 day 7 hours ago