When you think about Web 2.0 applications, often the most glamorous of them come to mind: the video of YouTube, the über-cool scrolling map of Google Maps, the geo-location functionality in Flikr. Often overlooked in such sites, however, is the humble HTML form that has undergone a big transformation with the popularization of Ajax technology.
In this article, I show you how to use the Prototype.js JavaScript library to solve common user experience problems as you augment forms with Ajax code.
Start with something really simple: a multi-field registration form that you want to submit with Ajax instead of using the regular Web form posting path. This simple forms page is shown in Listing 1.
Listing 1. index.html
<html>
<head>
<script src="prototype.js"></script>
</head>
<body>
<form id="myform">
<table>
<tr><td>First</td><td><input type="text" name="first"></td></tr>
<tr><td>Last</td><td><input type="text" name="last"></td></tr>
<tr><td>Email</td><td><input type="text" name="email"></td></tr>
</table>
<input type="button" onclick="dosubmit()" value="Submit">
</form>
<div id="result" style="padding:5px;">
</div>
<script>
function dosubmit( ) {
new Ajax.Updater( 'result', 'add.php', { method: 'post',
parameters: $('myform').serialize() } );
$('myform').reset();
}
</script>
</body>
</html>
|
At the top of the file, I include the prototype.js JavaScript file, which will do
all the Ajax work for me. After that is a traditional HTML form with three fields:
first, last, and email. Below that is the button that submits
the form using the dosubmit() JavaScript function.
This dosubmit() function uses the
Ajax.Updater class to post the data to the add.php
script. Then, you add options to the call. In this case, I set the method of
submission to post and add the parameters from the form
by using the serialize() method on the form. This
serialize() method is not standard JavaScript code: The
JavaScript library provides it.
The first item in the call to Ajax.Updater is the ID of
the <div> tag, which receives the HTML that the
add.php script returns. It's the easiest way to get notification back to the user
about what happened when he or she clicked the button.
The add.php script is shown in Listing 2.
Listing 2. add.php
Thanks <?php echo( $_POST['first'] ) ?> <?php echo( $_POST['last'] ) ?>!
|
All it does is echo back what was posted from the form. In reality, this would likely add a record to a database, but you can handle that kind of business logic work for yourself.
When I first load the form, I see something like Figure 1 in my browser.
Figure 1. A simple Ajax form
I then click Submit, which sends the form data to the page, and I get the returned HTML from the add.php page right below the Submit button, as shown in Figure 2.
Figure 2. The response following the submission
Another variation on forms with Ajax is the auto-fillin form, which updates field values depending on the value of some key fields—for example, typing a customer's ID, clicking a button, and then getting the current customer records in the other fields.
This type of auto-fillin form is shown in Listing 3.
Listing 3. index.html
<html>
<head>
<script src="prototype.js"></script>
</head>
<body>
<form id="myform">
<table>
<tr><td>ID</td><td><input
type="text" name="id"></td></tr>
<tr><td>First</td><td><input type="text"
name="first" id="elFirst"></td></tr>
<tr><td>Last</td><td><input type="text"
name="last" id="elLast"></td></tr>
<tr><td>Email</td><td><input type="text"
name="email" id="elEmail"></td></tr>
</table>
<input type="button" onclick="dofill()" value="Fill Fields">
</form>
<script>
function dofill( ) {
new Ajax.Updater( 'result', 'getdata.php',
{ method: 'post', parameters: $('myform').serialize(),
onSuccess: function( transport ) {
$('elFirst').value = transport.responseXML.getElementsByTagName('first')
[0].firstChild.nodeValue;
$('elLast').value = transport.responseXML.getElementsByTagName('last')
[0].firstChild.nodeValue;
$('elEmail').value = transport.responseXML.getElementsByTagName('email')
[0].firstChild.nodeValue;
} } );
}
</script>
</body>
</html>
|
I've added a new field to the original form from Listing 1
called ID. That's where you put in the customer's ID. Then, the new
dofill() function calls the getdata.php page, which
returns XML code that has the first name, last name, and e-mail address for the
given customer ID.
The onSuccess handler that I send to the
Ajax.Updater call uses the native Document Object Model
(DOM) functions available in all browsers to crack the downloaded XML data. It then
sets the value of the elFirst, elLast,
and elEmail <input> items to the values returned
in the XML.
The getdata.php page is shown in Listing 4.
Listing 4. getdata.php
<?php
header( "content-type: text/xml" );
$first = ' ';
$last = ' ';
$email = ' ';
if ( $_POST['id'] == '1' )
{
$first = 'Jack';
$last = 'Herrington';
$email = 'jherr@pobox.com';
}
?>
<data>
<first><?php echo( $first ) ?></first>
<last><?php echo( $last ) ?></last>
<email><?php echo( $email ) ?></email>
</data>
|
It's really just a stub method of code that normally calls the database to get the first name, last name, and e-mail address of the given record.
When I first bring up the page, I see something like Figure 3.
Figure 3. A fill-in form
I then type 1 in the ID field and click Fill
Fields, which goes off to the getdata.php page to get the first name, last
name, and e-mail address and update the corresponding fields. This is shown in
Figure 4.
Figure 4. The script fills in the fields based on the ID
For my next Ajax slight-of-hand, I create an update in-place to-do list.
One of the common demos for Ruby on Rails is the to-do list, which updates in-place. That means that the list of items is at the top of the page, and there is a text box below the list. When I type something into the text box and click Submit, the list at the top of the page updates with the new item without changing the page. And the text box in which I typed the text resets so that I can keep adding stuff with little effort.
It's actually a terribly difficult trick, so I figured I show off how to do it in PHP. The page for the to-do list is shown in Listing 5.
Listing 5. index.php
<html>
<head>
<script src="prototype.js"></script>
</head>
<body>
<div id="result" style="padding:5px;">
<?php
$fh = fopen( 'list.txt', 'r' );
while( $str = fgets( $fh ) ) {
?>
<?php echo( $str ); ?><br/>
<?php
}
?>
</div>
<form id="myform">
<input type="text" name="todo">
</form>
<input type="button" onclick="dosubmit()" value="Submit">
<script>
function dosubmit( ) {
new Ajax.Updater( 'result', 'add.php',
{ method: 'post', parameters: $('myform').serialize() } );
$('myform').reset();
}
</script>
</body>
</html>
|
Instead of holding the to-do list in a database, I just use a flat text file
called list.txt to store the items one per line. So, to put the list on the
top of the page, I simply open the file and read each line in to the
<div> tag with the ID of
result.
Below that is the form with the text input for the to-do item. And below that
is the button that calls the dosubmit() JavaScript
function. That JavaScript function uses the Ajax.Updater
class to call the add.php page, which adds the item and returns the new list with
the item appended to it.
This add.php script is shown in Listing 6.
Listing 6. add.php
<?php
$total = '';
$fh = fopen( 'list.txt', 'r' );
while( $str = fgets( $fh ) ) {
?>
<?php echo( $str ); ?><br/>
<?php
$total .= $str;
}
if ( array_key_exists( 'todo', $_POST ) )
{
?>
<?php echo( $_POST['todo'] ); ?><br/>
<?php
$fh = fopen( 'list.txt', 'w' );
fwrite( $fh, $total."\n".$_POST['todo'] );
fclose( $fh );
}
?>
|
My silly list of to-do items is shown in Listing 7.
Listing 7. list.txt
Get swim goggles
Practice swimming
Swim in race
|
When I first go to the page, I see something like Figure 5.
Figure 5. Preparing to add a to-do item
I type Finish in record time, and then click Submit.
Then, without a page refresh, I see the result shown in Figure 6.
Figure 6. The page after inserting a record
Of course, this isn't the only cool thing in the Rails demo of a to-do list. But it's one part of the Web 2.0 "wow" factor. Actually, if you haven't tried Rails, I strongly recommend it for any Web engineer. Even if you don't use it in your own projects, it's really interesting to see how applications are organized, how the model-view-controller (MVC) mechanism is used, and how easy the database persistence model is.
Another common Web requirement is the expanding list of fields. I call these expando lists.
What happens when you can have an unlimited number of keywords associated with a record? Well, one way to handle it is to use commas to separate the keywords. Another way is to have a button that adds new keyword fields on the fly to allow users to add as many keywords as they want. I use the second approach in Listing 8.
Listing 8. index.html
<html>
<head>
<script src="prototype.js"></script>
</head>
<body>
<form id="myform">
<table id="keytable">
<tr><td>Keyword</td><td><input type="text"
name="keyword_1"></td></tr>
</table>
</form>
<input type="button" onclick="addkeyword()" value="Add Keyword">
<input type="button" onclick="dosubmit()" value="Submit">
<div id="result" style="padding:5px;">
</div>
<script>
var nextkeyid = 2;
function addkeyword()
{
var elTR = $('keytable').insertRow( -1 );
var elTitleTD = elTR.insertCell( -1 );
elTitleTD.appendChild( document.createTextNode( 'Keyword' ) );
var elInput = document.createElement( 'input' );
elInput.type = 'text';
elInput.name = 'keyword_'+nextkeyid;
nextkeyid++;
var elInputTD = elTR.insertCell( -1 );
elInputTD.appendChild( elInput );
}
function dosubmit( ) {
new Ajax.Updater( 'result', 'add.php',
{ method: 'post', parameters: $('myform').serialize() } );
}
</script>
</body>
</html>
|
The real trick here is in the addkeyword() function,
which uses insertRow and insertCell
to create a new row in the table of keywords. Then, it uses
document.createElement to create a new input field to
hold the keyword. The Ajax.Updater code, which is called
when users click Submit, calls the add.php script. This add.php script simply
returns the list of keywords that the form gives it. This script is shown in
Listing 9.
Listing 9. add.php
Post Result:<br/>
<?php var_export( $_POST ) ?>
|
When I bring up the page in my browser, I first see something like Figure 7.
Figure 7. The keyword form with a single keyword
I click Add Keyword a few times to add new fields, then click Submit. I get the screen shown in Figure 8.
Figure 8. The keyword form after adding keywords and clicking Submit
This is an ideal way to allow users to add multiple values associated with a single record for things like phone numbers, keywords, and addresses. Another common use of Ajax is to implement login forms.
Login forms in Ajax are particularly cool, because they can give you instant feedback on whether you were able to log in and also because they can perform in-place logins. Imagine that you're looking at an article on which you want to comment, but you aren't logged in. With Ajax, you could log in while you're still looking at the article. If your credentials are accepted, a new form appears that you can use to add a comment. This process is much easier than having to track which article a person was looking at through the login flow to redirect the person after a successful login.
My simple version of the Ajax login is shown in Listing 10.
Listing 10. index.html
<html>
<head>
<script src="prototype.js"></script>
</head>
<body>
<form id="logform">
User: <input type="text" name="user"><br/>
Password: <input type="password" name="password"><br/>
<input type="button" onclick="login()" value="Login">
</form>
<div id="noway" style="display:none;">
No way!
</div>
<script>
function login() {
new Ajax.Request( 'login.php',
{
method: 'post',
postBody: $('logform').serialize(),
onSuccess: function( transport ) {
if( transport.responseText.match( /\<ok\/\>/ ) )
window.location = 'home.html';
else
$('noway').style.display='block';
}
} );
}
</script>
</body>
</html>
|
The User and Password fields are in the form at the top of the file.
Below those is a <div> tag with the ID
noway, which will be shown if the login is not accepted.
The login() JavaScript method makes the login attempt
using the Ajax.Request class. If the returned XML from
login.php is <ok />, the form redirects the user
to the home page. Otherwise, it brings up the noway
text.
The login.php code is shown in Listing 11.
Listing 11. login.php
<?php
header( 'Content-type: text/xml' );
if ( $_POST['user'] == 'jack' && $_POST['password'] == 'password' )
echo( "<ok/>" );
else
echo( "<bad/>" );
?>
|
This simple code checks against a hard-coded value for the user and password, then
returns ok if there is a match and
bad otherwise.
For completeness, the home page code is shown in Listing 12.
Listing 12. home.html
<html>
<body>
You are logged in and this is your home page.
</body>
</html>
|
To start the example in the browser, I bring up the page, type an invalid user name and password, and click Login. The result is shown in Figure 9.
Figure 9. The login page after typing an incorrect password
Then, when I change the user name and password to the correct values and click Login, I'm redirected to the home page shown in Figure 10.
Figure 10. The login page after typing the correct user name and password
My last example for this article is an XForms-based Ajax article.
I wouldn't consider myself an XForms expert, but I do find it a cool standard. In this article, I blend the client-side XForms stuff with Protoype.js and Ajax techniques.
The code for my simple XForms example is shown in Listing 13.
Listing 13. index.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xforms="http://www.w3.org/2002/xforms">
<head>
<title>XForms AJAX Example</title>
<xforms:model id="modelData">
<xforms:instance xmlns="">
<Data>
<First>First</First>
<Last>Last</Last>
<Email>email@email.com</Email>
</Data>
</xforms:instance>
</xforms:model>
<script src="prototype.js"></script>
</head>
<body>
<xforms:input ref="/Data/First">
<xforms:label>First: </xforms:label>
</xforms:input><br/>
<xforms:input ref="/Data/Last">
<xforms:label>Last: </xforms:label>
</xforms:input><br/>
<xforms:input ref="/Data/Email">
<xforms:label>Email: </xforms:label>
</xforms:input><br/><br/>
<button onclick="submit()">Submit</button>
<script>
function submit()
{
var m = $('modelData');
var base = m.getElementsByTagName('Data')[0];
var s = new XMLSerializer();
var data = ( s.serializeToString( base ) ).toString();
new Ajax.Updater( 'result', 'params.php',
{ method: 'post', parameters: 'data='+escape( data ) } );
}
</script>
<br/><br/>
<div id="result">
</div>
</body></html>
|
At the top of the file is the XML model for the XForm. Then, there is a set of
XForms labels and inputs that make up the form itself. The Submit button
calls the submit() JavaScript function. This function
then uses the XMLSerializer JavaScript object to turn
the XForms data model into an XML string, which is then posted to params.php. The
params.php script, shown in Listing 14, returns the XML as
HTML so that you can see what comes out of the page.
Listing 14. params.php
<?php
echo( htmlentities( $_POST['data'] ) );
?>
|
When I bring up the XForms page in my browser (with the XForms add-on installed), I see something like Figure 11.
Figure 11. An Ajax-enabled XForms page
Then, when I fill in the form and click Submit, I see something like Figure 12.
Figure 12. The page after clicking Submit
This shows the data model that was turned into XML text, sent across to params.php,
then returned into the result <div> tag.
You can do so much with Ajax to enable HTML forms, and this article just scratches the surface. However, it should give you some ideas and practical examples of what you can do in your own applications with relatively easy modifications to your page code.
| Description | Name | Size | Download method |
|---|---|---|---|
| Source code for forms application | x-ajaxxml9-forms.zip | 101KB | HTTP |
Information about download methods
Learn
-
PHP home page: Visit an invaluable resource for PHP programmers.
-
Prototype library: Explore this JavaScript Framework designed to ease development of dynamic Web applications.
-
Scriptaculous JavaScript library: Find display helpers and effects to make your Web site fly with this Prototype-based framework.
- The XForms add-on for Firefox: Make the XForms example in this article work for you.
-
Prototype.js documentation page: Get additional information on the Prototype JavaScript library with links to the official Prototype blog and many other resources.
-
jQuery: Explore another JavaScript library that provides similar functionality to Prototype.js.
-
Yahoo! User Interface Library: Check out Yahoo!'s toolkit for Ajax.
-
developerWorks XML zone: Learn all about XML at the developerWorks XML zone.
-
IBM XML certification: Find out how you can become an IBM-Certified Developer in XML and related technologies.
-
XML technical library: See the developerWorks XML Zone for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.
-
developerWorks technical events and webcasts: Stay current with technology in these sessions.
Discuss
- Participate in the discussion forum.
-
XML zone discussion forums: Participate in any of several XML-centered forums.
-
developerWorks XML zone: Share your thoughts: After you read this article, post your comments and thoughts in this forum. The XML zone editors moderate the forum and welcome your input.
Jack D. Herrington is a senior software engineer with more than 20 years of experience. He's the author of three books: Code Generation in Action, Podcasting Hacks, and PHP Hacks. He has also written more than 30 articles. You can reach Jack at jherr@pobox.com.
Comments (Undergoing maintenance)





