Download the
PhpAjax.zip archive.
Install it into your default website folder and set it up as
a NetBeans project as done in Php Basics.
There are two parts to this project which are divided into two subfolders:
dojo/ AJAX-driven examples using Dojo
native/ AJAX-driven examples using JavaScript directly
Of most interest to us is the Dojo code.
AJAX is an acronym for
Asynchronous Javascript And Xml.
The most important point that AJAX is about using
JavaScript to make asynchronous
server-side calls and change the appearance of a page
through client-side programming. As a result, much of
the usual server-side programming
activity due to page refreshes can be avoided.
Dojo Toolkit
Modern web applications often feature specialized effects
accomplished by JavaScript code written using client-side toolkits.
Writing in JavaScript directly tends to be quite tedious and
error-prone. The JavaScript language, per se, lacks capabilities
for modularity, sharing and testing. Errors are often difficult
to track down and browser differences become prominent.
The point about client-side toolkits is that they:
Minimally,
Dojo consists of only one JavaScript file which can be downloaded
from the Dojo Home Page.
It can also be downloaded as a zipped
file from the computer science web site:
Extract the zip file getting the folder dojo-release-1.3.2.
Windows
Linux (Ubuntu)
Windows
Move and rename this folder to this (or something of your choosing):
C:\dojo-release-1.3.2
Next, we'll make an alias URL for this location. Edit
the Apache configuration file, httpd.conf (from the Apache menu).
Go to the end of the file and add these lines:
Alias /dojolib "C:\dojo-release-1.3.2"
<Directory "C:\dojo-release-1.3.2">
Options FollowSymLinks
AllowOverride None
Order allow,deny
Allow from all
</Directory>
Then restart Apache (from the menu).
Testing/Debugging
JavaScript used in any setting is a difficult language to debug.
Fortunately there are some tools and techniques that make it possible.
The points about Firebug and console logging
mentioned below were derived from this document:
Use Firefox and install
Firebug.
Firefox's built-in debugger is the Error Console, which
is often acceptable in itself. One accesses the error
information through
Tools Error Console.
The problem is that only JavaScript errors/warnings will show up in the
Error Console, but much more detailed information is usually needed to
debug AJAX-related programming.
Firebug fills the gap. Once installed, access it by:
Tools Firebug Open Firebug.
HTML visibility
To illustrate the point I'm trying to make,
if use JavaScript to generate HTML code (as we'll do below) and
you attempt to "see" the underlying code via View Page Source, you
will discover that it's not shown.
However you will be able to see this JavaScript-generated code through Firebug's HTML tab.
Alerts and console logging
Use JavaScript alert statements to print out information
and pause the browser before continuing.
Often better than alerts is Firebug's console logging. In Firebug, click
the Console tab. You first have to activate the console, but once activated
you can see the output of the following statements each of
which produces a different colorization effect in the logging:
console.log("Here am I sitting in a tin can, far above the world.")
console.debug("Take your protein pills and put your helmet on.")
console.info("Now it's time to leave the capsule if you dare.")
console.warn("Can you hear me Major Tom?")
console.error("Your circuit's dead, there's something wrong!")
You need to remember to deactivate (comment out)
the console logging statements when used without Firebug, but if
you're using this in conjuction with Dojo, this initial loading will
avoid errors:
JavaScript's class creation is not declaration-based like Java or Php
but is done completely "on the fly."
This means
that JavaScript permits you to add new members at will
to almost any existing object.
A downside of this feature is that most misspellings
of a subobject will not generate
an error, such as:
dojo.byId('myelt').innerHtml = "hello"
when you really meant:
dojo.byId('myelt').innerHTML = "hello"
If it appears that "nothing is happening when it should," this is often
the source of the problem.
Testing the Php handlers independently
It is sometimes useful to test the Php server-side handlers "by hand", independently
of their being called by a JavaScript activation.
To do so, make sure that the handlers accept both GET and POST queries
equally, regardless of their intended usage. Namely, define
$param = array_merge( $_GET, $_POST );
and then use $param['name'] instead of, say,
$_POST['name'].
Then pass the incoming date to the handler script via the
query string into the browser command line. For example, assuming that
xx and yy are the relevant incoming query parameters, try:
The JavaScript language shares much of the same syntax as the Java
language minus the strong typing. Strings can be delimited by double or
single quotes and there is only one numeric type (represented by
a double).
Other than language details, the most significant difference with Java
is that JavaScript is built in to the standard browsers
in order program all the browser features according to the
so-called Document Object Model (DOM).
The DOM supports at its top level the window object, representing
the entire browser. The subobjects of window are these:
document (referring to the HTML document
contents),
navigator (information about the browser type, version, etc),
location (information about the URL being accessed),
history (access to previous pages),
status (the status bar at bottom of browser),
frames[] (access to frames in a frame-based layout).
For example, the expression window.document refers
to the document part of window object.
In all cases, JavaScript
permits the omission of the top-level "window." syntax
and simply write "document" (and others).
Introducing JavaScript code into HTML
JavaScript code is typically
made available through the script tags:
<script type="text/javascript">
<!--
JavaScript code here
// -->
</script>
These lines make a browser which does not recognize JavaScript
ignore the code between:
<!--
and
// -->
Alternatively, an external code file, say
extern-code.js
can be loaded with this script tag usage:
Our JavaScript code of interest consists mostly of functions called by
JavaScript events. For example, we can create a "generic" button which
calls a JavaScript function as follows:
function my_button_handler()
{
// what to do when the button is pressed
}
Reading/Writing HTML elements from JavaScript
Within JavaScript code we need to be able to read and write data from
the HTML elements.
JavaScript provides a number of ways to obtain a DOM object
for an HTML element,
but perhaps the simplest and direct way
is by assigning an id attribute
to the element and obtaining an object for it with the function
document.getElementById.
For example, if we define:
<input type="text" id="tf" />
then, within JavaScript we can obtain an
object representing this textfield
with the statement:
var tf = document.getElementById( "tf" )
All the attributes of the original HTML element become available to
the JavaScript code as subobjects. For example,
to put "HELLO" into the textfield, we write:
tf.value = "HELLO"
Boolean attributes behave
like boolean values in JavaScript. For example, if we have:
<input type="checkbox" id="cb" /> Some Checkbox
Then we can determine if this checkbox is checked with this code:
var cb = document.getElementById( "cb" )
if (cb.checked) { ... }
The innerHTML JavaScript subobject
A useful
JavaScript subobject that does not correspond to an attribute
is innerHTML. It represents the
content between
the start and end tags of an HTML element. For example if we have
<p id="output">*************</p>
then this JavaScript code
replaces the paragraph's current content by Hello World.
var output = document.getElementById( "output" )
output.innerHTML = "Hello World"
This example illustrates simple JavaScript manipulation of HTML
elements in which a selection list's value is displayed in an
HTML span element.
show-choice.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Show Choice</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript">
<!--
function showChoice(){
var sel = document.getElementById("sel")
var output = document.getElementById("output");
output.innerHTML = sel.value
}
// -->
</script>
</head>
<body>
<h3>Selection with JavaScript Display</h3>
<select id="sel" onchange="showChoice()">
<option value="">--- choose ---</option>
<option value="1">AA</option>
<option value="2">BB</option>
<option value="3">CC</option>
<option value="4">DD</option>
<option value="5">EE</option>
</select>
Selected value: <span id="output"></span>
</body>
</html>
The textarea value subobject
The textarea supports data entry using
the value subobject despite the fact that value
is not an attribute.
For example if we have:
<textarea id="ta" rows="10" cols="40"></textarea>
then, to place text
within this textarea, we would use JavaScript code like this:
var ta = document.getElementById( "ta" )
ta.value = "Text placed\nwithin the textarea";
Because textarea has start and end tags, it also supports the
innerHTML subobject, but this appears to be less reliable
(at least for version 7 of Internet Explorer), and the assignment:
ta.innerHTML = "Text placed\nwithin the textarea";
may not respect the newline character!
AJAX call to a server-side program
The main point of AJAX is that we can use JavaScript to call
server-side program and deliver the result(s) directly to HTML elements.
The key idea is the existence of a so-called
JavaScript Http Request object which
can invoke a server-side activation.
The definition of this object is browser-dependent and so a common way
to create such an object is via a JavaScript function like this:
function GetXmlHttpObject() {
var obj = null;
if (window.XMLHttpRequest)
obj = new XMLHttpRequest();
else if (window.ActiveXObject)
obj = new ActiveXObject("Microsoft.XMLHTTP");
return obj;
}
The usage for a simple GET-activated call might go as follows:
url = /* server-side program execution */
after = function() {
/* what to do after server-side call has completed */
}
xmlHttp = GetXmlHttpObject();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState != 4) return
if (requestTimer != null) clearTimeout(requestTimer)
if (xmlHttp.status != 200) {
alert("server error")
return
}
after();
}
xmlHttp.open("GET", url, true)
xmlHttp.send(null)
The activation of the server-side url actually takes place in the
last two lines:
xmlHttp.open("GET", url, true)
xmlHttp.send(null)
but the JavaScript after function,
is used before this server-side call.
This is the essence of the asynchronous behavior of AJAX activations:
the after function is part of a
callback of the xmlHttp object.
To clarify the asynchronous nature, the client-side
JavaScript program activates the url
and then "continues on" without waiting for the completion of
the activation, and sometime later the after function takes effect.
Server Output, JSON & XML
In most cases we are interested in the data the server can retrieve
for us. Assuming the global availability
of the JavaScript Http Request object, xmlHttp, as indicated above,
the server-side raw output is available to us as:
var data = xmlHttp.responseText
For simple output, this is sufficient; for
structured output, there are two possible formats: JSON or XML.
JSON format
The
JSON (JavaScript Object Notation)
represents data as certain Java "object literals". For example:
{ x: 222, y: "hello" } -- an "object" with members x and y
[ 12, "hello", 15 ] -- an array
[ {x: 12, y: "aa"}, {x: 17, y: "bb"} ] -- an array of objects
We can convert the JSON object into JavaScript directly using
JavaScript's eval operation used like this:
var data = eval( "(" + xmlHttp.responseText + ")" );
After that, we can access data
as a JavaScript object like this
(respectively):
data.x, data.y -- an "object" with members x and y
data[0], data[1], ... -- an array
data[0].x, data[1].y -- an array of objects
Both Java and JavaScript support a "comma at the end" of the last
array element, e.g.
[ 12, "hello", 15 ] is the same as [ 12, "hello", 15, ]
However, Internet Explorer, at least is some versions, appears
to put an extra null at the end of array in the latter case.
So it's better to avoid the "comma at the end."
XML format
XML format is assumed to be well known. The server-side program must
send XML format in the strictest sense. For example, this static
Php program might be used to send a booklist:
This is precisely the situation where the Php "short open tag" will
get you into trouble since it conflicts with the xml header tag.
On the JavaScript side, you have to realize the data in a different
way as:
var xmlData = xmlHttp.responseXML
Once you have data in this format, extracting the values is none too
easy with code starting something like this:
var books = xmlData.getElementsByTagName("book");
and so on.
Prefer JSON to XML
It is more-or-less universally acknowleged that JSON format is
better than XML for reasons both of efficiency and simplicity.
In retrospect "AJAX" probably should have been "AJAJ";
however the former definitely sounds cooler.
Dojo Ajax Examples
The dojo
folder illustrates AJAX-driven calls using the Dojo toolkit.
The initial activation of Dojo is via the script usage:
This file uses dojo with member function calls like this
GET activation of a URL
dojo.xhrGet(
{
url: "SERVER SIDE PROGRAM",
content: { /* parameter: value pairs */ },
load: function (response, ioArgs) {
// this is activated after the server-side completion
// response is the server output
},
error: function (response, ioArgs) {
// what to do if something goes wrong, typically:
alert("error: " + response)
}
}
)
The dojo.xhrGet function takes a single argument expressed
as a JSON object. Here are some variations:
The call dojo.xhrPost does the same operation, except
using a POST activation instead of GET.
In certain cases no server-side input is necessary, and so
the content field can be dropped.
The server can send back structured JSON output in which case
the response can be automatically transformed to a JavaScript object
simply by adding the field to the call:
handleAs: "json",
An extremely useful feature is that a
dojo.xhrGet (or dojo.xhrPost) call
can automatically
create name/value parameter pairs from the named elements
(i.e. those with name="...") within a form.
We simply use the field:
form: formId,
where the formId comes from the HTML element:
<form id="formId" ... >
These parameters can replace or augment those defined
by the content field.
In certain cases the server-side output is not needed, and so
the load field can be dropped.
In certain cases, it is useful to have the client wait for
completion of the server-side activation, such as the initial
loading of the document. To achieve this affect, add the field:
sync: true,
One additional feature
is the dojo.byId call as a convenient replacement of
document.getElementById; e.g.,
dojo.byId('the ID of some element').innerHTML = SOMETHING
Here is the code:
dojo/index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Dojo Examples</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="../my.css" type="text/css" />
<script type="text/javascript" src="/dojolib/dojo/dojo.js"
djConfig="parseOnLoad:true"></script>
<script type="text/javascript" src="examples.js"></script>
<script type="text/javascript">
dojo.addOnLoad( // an alternative to using onload="..." in body
function() {
ex3_init()
ex4_init()
}
)
</script>
</head>
<body>
<a href="javascript:location.replace('..')">UP</a>
<h3>Examples of usage of Dojo-AJAX</h3>
<h4>Example 1: server-side script "get-time.php"</h4>
<input type="submit" onclick="ex1_handler()" value="Get Times" />
<p>
client-side time: <span id="ex1_c"></span> <br />
server-side time: <span id="ex1_s"></span>
</p>
<h4>Example 2: server-side script "get-book.php"</h4>
<p>This transmission format is called JSON.
</p>
<table cellpadding="5px" border="1">
<tr><td>title:</td> <td id="title"> </td></tr>
<tr><td>type:</td> <td id="type"> </td></tr>
<tr><td>qty:</td> <td id="qty"> </td></tr>
</table>
<p>
<select id="id">
<option value="1">1</option>
<option value="2">2</option>
</select>
<button onclick="ex2_set()">Get Book</button>
</p>
<h4>Example 3: server-side script "get-set-session-value.php"</h4>
<p>Type values into both textfields. Click the button. <br />
Go UP and back here to observe the second value preserved.
</p>
<input type="text" />
<input type="text" id="ex3" />
<button onclick="ex3_set()">Save Second as Session "xx"</button>
<h4>Example 4: server-side script "get-options.php"</h4>
<p>The selection list is populated from JavaScript array of
objects sent by the server-side script (JSON again).
</p>
<select id="ex4" onchange="ex4_set()"></select>
Value: <span id="ex4_value"></span>
<h4>Example 5: server-side script "show-post.php"</h4>
<p>
Dojo-AJAX presented more like a usual form submission.<br />
Enter data into the fields and press the button.
</p>
<form id="ex5_form" method="post">
<table cellpadding="5px">
<tr><td>title:</td> <td><input type="text" name="title"/></td></tr>
<tr><td>type:</td> <td><input type="text" name="type"/></td></tr>
<tr><td>qty:</td> <td ><input type="text" name="qty"/></td></tr>
</table>
<button onclick="ex5_handler();return false;">
Send Named Params
</button>
</form>
<p>
<textarea id="book_content" rows="8" cols="30" readonly></textarea>
</p>
</body>
</html>
dojo/examples.js
function ex1_handler() {
dojo.xhrGet(
{
url: "../handlers/get-time.php",
load: function (response, ioArgs) {
dojo.byId('ex1_s').innerHTML = response
},
error: function (response, ioArgs) {
alert("error: " + response)
}
}
)
dojo.byId('ex1_c').innerHTML = new Date()
}
function ex2_set() {
var id_val = dojo.byId("id").value
dojo.xhrGet(
{
url: "../handlers/get-book.php",
content: {
id: id_val
},
handleAs: "json",
load: function(response, ioArgs) {
dojo.byId("title").innerHTML = response.title;
dojo.byId("type").innerHTML = response.type;
dojo.byId("qty").innerHTML = response.qty;
},
error: function (response, ioArgs) {
alert("error: " + response)
}
}
)
}
function ex3_set() {
var ex3_value = document.getElementById("ex3").value
dojo.xhrGet(
{
url: "../handlers/get-set-session-xx.php",
content: {
xx: ex3_value
},
error: function (response, ioArgs) {
alert("error: " + response)
}
}
)
}
function ex3_init() {
dojo.xhrGet(
{
url: "../handlers/get-set-session-xx.php",
sync: true,
load: function(response, ioArgs) {
dojo.byId("ex3").value = response
},
error: function (response, ioArgs) {
alert("error: " + response)
}
}
)
}
// Example 4 also illustrates how the handler function can be
// treated externally from the dojo.xhr call
function display_options(response, ioArgs) {
// response is an array of (value,label) objects
// tack the intial ( no choice ) object on the front:
// There is also a list.add function which seems more "Java-like",
// but it may have to be used differently in different browsers
var selections = [ {value: 0, label: "-----"} ].concat( response );
var list = dojo.byId("ex4");
for (i = 0; i < selections.length; ++i) {
list.options[i] = new Option(selections[i].label, selections[i].value)
}
list.options[selections.length - 1].selected = true;
}
function ex4_init() {
dojo.xhrGet(
{
url: "../handlers/get-options.php",
handleAs: "json",
sync: true,
load: display_options,
error: function (response, ioArgs) {
alert("error: " + response)
}
}
)
}
function ex4_set() {
dojo.byId("ex4_value").innerHTML = dojo.byId("ex4").value;
}
function ex5_handler() {
dojo.xhrPost(
{
url: '../handlers/show-post.php',
form: "ex5_form",
load: function (response, ioArgs) {
dojo.byId('book_content').value = response
},
error: function (response, ioArgs) {
alert("error: " + response)
}
}
)
}
The server-side scripts used are these:
The native folder
illustrates AJAX-driven calls using plain JavaScript
encapsulated in the JavaScript file ajax.js.
This file provides two functions usable for making an AJAX activations:
The url paramater, in our case, will be a Php program;
params is a string of
encoded parameters of the form:
name1=value1&name2=value2&...
and after is a JavaScript function to be activated
when the server-side script is complete. A function
urlEncode(str)
is a helper for creating the correct encoding.
The JavaScript Http Request object, xmlHttp,
is a global variable and
can therefore be used in the after function
to obtain the server-side standard output as:
xmlHttp.responseText
Here are the sample files:
native/index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Native Examples</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="../my.css" type="text/css" />
<script type="text/javascript" src="ajax.js"></script>
<script type="text/javascript" src="examples.js"></script>
</head>
<body onload="init()">
<a href="javascript:location.replace('..')">UP</a>
<h3>Examples of native usage of AJAX</h3>
<h4>Example 1: server-side script "get-time.php"</h4>
<input type="submit" onclick="ex1_handler()" value="Get Times" />
<p> client-side time: <span id="ex1_c"></span> <br />
server-side time: <span id="ex1_s"></span> </p>
<h4>Example 2: server-side script "get-book.php"</h4>
<p> This transmission format is called JSON. </p>
<table cellpadding="5px" border="1">
<tr><td>title:</td> <td id="title"> </td></tr>
<tr><td>type:</td> <td id="type"> </td></tr>
<tr><td>qty:</td> <td id="qty"> </td></tr>
</table>
<p>
<select id="id">
<option value="1">1</option>
<option value="2">2</option>
</select>
<button onclick="ex2_set()">Get Book</button>
</p>
<h4>Example 3: server-side script "get-set-session-value.php"</h4>
<p> Type values into both textfields. Click the button. <br />
Go UP and back here to observe the second value preserved. </p>
<input type="text" />
<input type="text" id="ex3" />
<button onclick="ex3_set()">Save Second as Session "xx"</button>
<h4>Example 4: server-side script "get-options.php"</h4>
<p>
The selection list is populated from JavaScript array of
objects sent by the server-side script (JSON again).
</p>
<select id="ex4" onchange="ex4_set()"></select>
Value: <span id="ex4_value"></span>
</body>
</html>
native/examples.js
function ex1_handler() {
ajaxGet(
"../handlers/get-time.php",
null,
function() {
document.getElementById("ex1_s").innerHTML = xmlHttp.responseText
}
)
document.getElementById("ex1_c").innerHTML = new Date()
}
function ex2_set() {
var id_val = document.getElementById("id").value
ajaxGet(
"../handlers/get-book.php",
"id=" + id_val,
function() {
var data = eval( "(" + xmlHttp.responseText + ")" )
document.getElementById("title").innerHTML = data.title;
document.getElementById("type").innerHTML = data.type;
document.getElementById("qty").innerHTML = data.qty;
}
)
}
function ex3_set() {
var ex3_value = document.getElementById("ex3").value
ajaxGet( "../handlers/get-set-session-xx.php",
"xx=" + urlEncode(ex3_value), null )
}
function ex4_set() {
var list = document.getElementById("ex4");
var value_elt = document.getElementById("ex4_value");
value_elt.innerHTML = list.value;
}
function display_options() {
var data = eval( "(" + xmlHttp.responseText + ")" )
var selections = [ {value: 0, label: "-----"} ].concat( data );
var list = document.getElementById("ex4");
for (i = 0; i < selections.length; ++i) {
list.options[i] = new Option(selections[i].label, selections[i].value)
}
list.options[selections.length - 1].selected = true;
}
function list_init() {
ajaxGet( "../handlers/get-options.php", null, display_options )
}
/* One of the downsides of my "native" approach is the inability
* to easily do two AJAX calls on one event. I have to force them
* to be executed in sequence as is done here.
*/
function init() {
ajaxGet(
"../handlers/get-set-session-xx.php",
null,
function() {
// initialize the ex2 textfield from session value
document.getElementById("ex3").value = xmlHttp.responseText
list_init(); // initialize the ex3 list
}
)
}
native/ajax.js
/* ajax.js
* author: Robert Kline
*/
var xmlHttp
function GetXmlHttpObject() {
var obj = null;
if (window.XMLHttpRequest)
obj = new XMLHttpRequest();
else if (window.ActiveXObject)
obj = new ActiveXObject("Microsoft.XMLHTTP");
return obj;
}
function ajaxGet(url, params, after)
{
xmlHttp = GetXmlHttpObject();
if (xmlHttp == null) return;
var requestTimer
xmlHttp.onreadystatechange = function()
{
if (xmlHttp.readyState != 4)
return
if (requestTimer != null)
clearTimeout(requestTimer)
if (xmlHttp.status != 200) {
alert("server error")
return
}
if (after != null) after();
}
if (params != null) url += "?" + params
requestTimer =
setTimeout( function() { xmlHttp.abort(); alert("time out") }, 5000 )
xmlHttp.open("GET", url, true)
xmlHttp.send(null)
}
function ajaxPost(url, params, after)
{
xmlHttp = GetXmlHttpObject();
if (xmlHttp == null) return;
var requestTimer
xmlHttp.onreadystatechange = function()
{
if (xmlHttp.readyState != 4)
return
if (requestTimer != null) clearTimeout(requestTimer)
if (xmlHttp.status != 200) {
alert("server error")
return
}
if (after != null) after();
}
if (params != null) url += "?" + params
requestTimer =
setTimeout( function() { xmlHttp.abort(); alert("time out") }, 5000 )
xmlHttp.open( "POST", url, true );
xmlHttp.setRequestHeader(
'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'
)
xmlHttp.send(params)
}
function urlEncode(str) {
var newstr = escape(str);
newstr = newstr.replace(/\+/g, "%2B");
newstr = newstr.replace(/\//g, "%2F");
return newstr;
}