Level: Intermediate Jack D Herrington (jherr@pobox.com), Senior Software Engineer, Leverage Software Inc.
22 Jan 2008 Augmenting your HTML forms with Asynchronous JavaScript™ + XML
(Ajax) callbacks to the server is a practical way to add Web 2.0 functionality to your
application. Discover a variety of techniques to add Ajax code and enhance the user
experience for PHP applications.
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.
Simple Ajax form submission
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.
Lists that update in-place
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.
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
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.
XForms Ajax
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.
Conclusion
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.
Download | Description | Name | Size | Download method |
|---|
| Source code for forms application | x-ajaxxml9-forms.zip | 101KB | HTTP |
|---|
Resources Learn
Discuss
About the author
Rate this page
|