To simply and efficiently ***use*** AJAX on your PHP-driven websites, without having to plunge into theosophical arguments about XML, DTDs or frameworks.
When it comes to AJAX, discussions can turn into things like this ERT Forum's topic because the proposed Article didn't mention "AJAX frameworks", "JSON and others alternatives", "Using data islands in conjunction with XMLDOM"...
Well, I don't give a s**t and will not enter this ranting discussion.
All I want is to be able to USE the so-called buzz/hype/must-do technique called AJAX, and to be able to do this SIMPLY for a very SIMPLE purpose : to be able to trigger client-side queries to the server, whilst using PHP on the server to generate the client's DHTML code. For instance to enable the user to update a tree menu's labels stored in a RDBMS on the server. Or to dynamically get from that very same RDBMS data based on the current selection on a listbox, triggered by OnChange() event. I did all of that using the hereafter-described technique.
You could indeed say that what I need is "only Remote Scripting", but to me and AFAIK, AJAX is nothing more than "XMLHttpRequest + Javascript" (QUOTE : "AJAX stands for Asynchronous JavaScript And XML" ). Call it "Remote Scripting" if you like. I personally don't care at all.
You can get a full description of AJAX in English (presents the AJAX basic use the same way as the above ERT Discussion) at http://www.javarss.com/ajax/j2ee-ajax.html and you can get a remarquable description of the XMLHttpRequest Object at http://openweb.eu.org/articles/objet_xmlhttprequest/ (in French, in Google cache if link stays down, and I made a local copy just in case)
The first part does exist, I found it :D
It's called SACK ( Simple AJAX Code-Kit ) ©2005 Gregory Wild-Smith at http://www.twilightuniverse.com and it does it all for 4491 bytes ;-)
This is the file ajax.js (save as after opening link, or directly do Save Link As... and rename to .js :D )
/* Simple AJAX Code-Kit (SACK) */
/* ©2005 Gregory Wild-Smith */
/* www.twilightuniverse.com */
/* Software licenced under a modified X11 licence, see documentation or authors website for more details */
function sack(file){
this.AjaxFailedAlert = "Your browser does not support the enhanced functionality of this website, and therefore you will have an experience that differs from the intended one.\n";
this.requestFile = file;
this.method = "POST";
this.URLString = "";
this.encodeURIString = true;
this.execute = false;
this.onLoading = function() { };
this.onLoaded = function() { };
this.onInteractive = function() { };
this.onCompletion = function() { };
this.createAJAX = function() {
try {
this.xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (err) {
this.xmlhttp = null;
}
}
if(!this.xmlhttp && typeof XMLHttpRequest != "undefined")
this.xmlhttp = new XMLHttpRequest();
if (!this.xmlhttp){
this.failed = true;
}
};
this.setVar = function(name, value){
if (this.URLString.length < 3){
this.URLString = name + "=" + value;
} else {
this.URLString += "&" + name + "=" + value;
}
}
this.encVar = function(name, value){
var varString = encodeURIComponent(name) + "=" + encodeURIComponent(value);
return varString;
}
this.encodeURLString = function(string){
varArray = string.split('&');
for (i = 0; i < varArray.length; i++){
urlVars = varArray[i].split('=');
if (urlVars[0].indexOf('amp;') != -1){
urlVars[0] = urlVars[0].substring(4);
}
varArray[i] = this.encVar(urlVars[0],urlVars[1]);
}
return varArray.join('&');
}
this.runResponse = function(){
eval(this.response);
}
this.runAJAX = function(urlstring){
this.responseStatus = new Array(2);
if(this.failed && this.AjaxFailedAlert){
alert(this.AjaxFailedAlert);
} else {
if (urlstring){
if (this.URLString.length){
this.URLString = this.URLString + "&" + urlstring;
} else {
this.URLString = urlstring;
}
}
if (this.encodeURIString){
var timeval = new Date().getTime();
this.URLString = this.encodeURLString(this.URLString);
this.setVar("rndval", timeval);
}
if (this.element) { this.elementObj = document.getElementById(this.element); }
if (this.xmlhttp) {
var self = this;
if (this.method == "GET") {
var totalurlstring = this.requestFile + "?" + this.URLString;
this.xmlhttp.open(this.method, totalurlstring, true);
} else {
this.xmlhttp.open(this.method, this.requestFile, true);
}
if (this.method == "POST"){
try {
this.xmlhttp.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
} catch (e) {}
}
this.xmlhttp.send(this.URLString);
this.xmlhttp.onreadystatechange = function() {
switch (self.xmlhttp.readyState){
case 1:
self.onLoading();
break;
case 2:
self.onLoaded();
break;
case 3:
self.onInteractive();
break;
case 4:
self.response = self.xmlhttp.responseText;
self.responseXML = self.xmlhttp.responseXML;
self.responseStatus[0] = self.xmlhttp.status;
self.responseStatus[1] = self.xmlhttp.statusText;
self.onCompletion();
if(self.execute){ self.runResponse(); }
if (self.elementObj) {
var elemNodeName = self.elementObj.nodeName;
elemNodeName.toLowerCase();
if (elemNodeName == "input" || elemNodeName == "select" || elemNodeName == "option" || elemNodeName == "textarea"){
self.elementObj.value = self.response;
} else {
self.elementObj.innerHTML = self.response;
}
}
self.URLString = "";
break;
}
};
}
}
};
this.createAJAX();
}
var ajax = new sack(); //VGR26042006 ADDed declaration here
My PHP scripts would generate such a JavaScript Code :
<script language="JavaScript" type="text/javascript" src="ajax.js"></script>
function showUpdate() {
document.getElementById('ajaxMessage').innerHTML = ajax.response;
}
function hideEdit() {
var editObj = editEl.previousSibling;
if (editObj.value.length>0) {
editEl.innerHTML = editObj.value;
var parentid=editEl.getAttribute("id_parent");
var extrastring=(parentid!=null)?"&id_parent="+parentid:"";
ajax.requestFile = 'updateNode.php?updateNode='+editObj.id.replace(/[^0-9]/g,'') + '&newValue='+editObj.value + extrastring;
ajax.onCompletion = showUpdate; // Specify function that will be executed after file has been found
ajax.runAJAX(); // Execute AJAX function
}
}
You must include such a call via DOM manipulation (like in here) or simply via a OnMouseDown="hideEdit();"
directly on the HTML tag
anyTag.onmousedown = hideEdit;
This is the file referenced above, called 'updateNode.php'
This time, the PHP script just outputs data the its standard output (in this case, the client's browser) where the XMLHttprequest Object is listening
<?php
//
// updateNode.php
//
//VGR06032006 Finished
//VGR19032006 ADDed sécurité contre les fausses insertions ('Nouvelle...' transmis)
//VGR01092006 MOD for publication on EEE.org ( www.europeanexperts.org ) and ERT (www.expertsrt.com | net )
//
// REM reçoit updateNode=2&newValue=dd en GET
//
// TODO : Nil
//
if ( isset($_GET['newValue']) AND isset($_GET['updateNode']) ) {
require_once('header.inc.php'); //VGR01092006 defines DB settings etc
$loclinkadd=txt('Nouvelle...'); //VGR19032006 ADDed : this is the default value contained on the client side
$newValue=addslashes($_GET['newValue']);
$updateNode=$_GET['updateNode'];
$id_parent=(isset($_GET['id_parent']))?$_GET['id_parent']:$updateNode;
if ($newValue<>$loclinkadd) { //VGR19032006 ADDed sécurité (else NOP)
if ($updateNode>$iszero) { // new category
$query="select max(id) AS a from $dbTableCategories;";
$result=mysql_query($query,$linkId) or die("bad query '$query' : ".mysql_error());
$res=mysql_fetch_array($result);
$nextid=$res['a']+1;
$command='insert into';
if ($id_parent==0) { // new main category
$extracmd=",id_parent=$nextid,id_l={$_SESSION['sess_langue']},id=$nextid";
} else $extracmd=",id_parent=$id_parent,id_l={$_SESSION['sess_langue']},id=$nextid";
$_GET['updateNode']=$nextid;
} else { $command='update'; $extracmd=" where id=$updateNode AND id_l={$_SESSION['sess_langue']}"; } // normal update
$query="$command $dbTableCategories set description='$newValue'{$extracmd};";
$result=mysql_query($query,$linkId) or die("bad query '$query' : ".mysql_error());
echo "update done ('$command') newval={$_GET['newValue']} pour id={$_GET['updateNode']}";
} // else NOP
else echo "NOP";
} else echo "appel incorrect.";
?>
My PHP scripts would generate such a JavaScript Code :
<script language="JavaScript" type="text/javascript" src="ajax.js"></script>
This, if used, must be generated before the rest of the DHTML BODY Code
<div id="ajaxMessage"></div>
This time, it's directly on the HTML tag
echo " <img src=\"$ModFavIcon\" align=\"middle\" Onclick=\"EditFav({$res['id']});\" alt=\"Edit\" title=\"$loctxtModFav\" class=\"clickableimg\">";
This code can be generated at any point in the BODY (in my case, at the end)
<div id="fav_popup" class="opaque popup" OnKeyDown="defaultvalid(event);">
<script language="JavaScript" type="text/javascript">
function defaultvalid(event) {
if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {
appliquer(false);
return false;
} else return false;
}
_dom=(document.all?3:(document.getElementById?1:(document.layers?2:0)));
function hide_fav_popup() {
fav_popup_layer=(_dom!=3)?document.getElementById('fav_popup'):fav_popup;
form=document.forms.addfav;
document.getElementById("deleteButton").style.visibility = "hidden";
document.getElementById("deleteButton").parentNode.disabled = "disabled";
fav_popup_layer.style.visibility="hidden";
}
function appliquer(parDel) { //VGR01092006 REM this triggers a PHP script to perform the DB update/insert/delete
form=document.forms.addfav;
var thesaisie=form.formlink.value;
var theref=escape(trim(thesaisie)); //VGR REM equivalent to urlencode()
var thetitre=escape(form.formtitre.value);
var thecomm=escape(form.formcomm.value);
var thekeyw1=escape(form.formkeyw1.value);
var thekeyw2=escape(form.formkeyw2.value);
var thekeyw3=escape(form.formkeyw3.value);
var theid=form.previd.value;
fav_popup_layer=(_dom!=3)?document.getElementById('fav_popup'):fav_popup;
fav_popup_layer.style.visibility="hidden";
var delme=0;
if ( (theref!=escape('DefaultValue'))&&(theref!='') ) {
// data protection
if (thetitre=='') thetitre=thesaisie.replace(/^http:\/\/www\.(.*)$/g,"$1");
var thenewval=thesaisie.replace(/^http:\/\/www\.(.*)\.(com|org|net|fr|biz|info)$/g,"site de $1");
if (parDel) delme=1;
// apply
parent.frames[1].location.href="real_index.php?previd="+theid+"&addfav="+theref+"&titre="+thetitre+"&comm="+thecomm+"&keyw="+thekeyw1+"|"+thekeyw2+"|"+thekeyw3+"&delme="+delme;
} // else NOP
}
var curid=0;
function remplit() { //VGR01092006 REM this gets the data from the Server via AJAX
var id=curid;
form=document.forms.addfav;
//for tests only : document.getElementById('ajaxMessage').innerHTML = ajax.response;
var reponse=ajax.response;
// on a reçu : res['favori'].'|'.res['titre'].'|'.res['commentaire'].'|'.res['motsclefs'];
var bidule=reponse.split('|');
form.formlink.value=bidule[0];
form.formtitre.value=bidule[1];
form.formcomm.value=bidule[2];
var bidule2=bidule[3].split(' '); // first three words
form.formkeyw1.value=bidule2[0];
form.formkeyw2.value=(bidule2.length>1)?bidule2[1]:"";
form.formkeyw3.value=(bidule2.length>2)?bidule2[2]:"";
form.previd.value=id;
// set the delete button
document.getElementById("deleteButton").style.visibility = "visible";
document.getElementById("deleteButton").parentNode.disabled = "";
form.editval.value=1;
// display
topPosition = parseInt(document.body.scrollTop) + 10;
fav_popup_layer=(_dom!=3)?document.getElementById('fav_popup'):fav_popup;
fav_popup_layer.style.visibility = "visible";
fav_popup_layer.style.cursor="text";
document.forms.addfav.formtitre.focus();
}
function EditFav(id) {
// get data via AJAX ;-)
ajax.requestFile = 'getFavorite.php?id='+id;
curid=id;
ajax.onCompletion = remplit; // Specify function that will be executed after file has been found
ajax.runAJAX();// Execute AJAX function
}
</script>
<form name="addfav" id="addfav" action="" method="GET">
<input type="hidden" name="editval" id="editval" value="0">
<input type="hidden" name="previd" id="previd" value="0">
<!--unused in fact-->
<table width="100%"><tbody><tr><td align="right" width="*"><center><b>$loctxtaddfav</b></center></td><td align="right" width="15"> <a href="javascript:hide_fav_popup();"><img src="$courant/images/cross_icon.jpg" width="9" height="9" border="0" alt="X"></a></td></tr>
<tr><td align="right">{$loctxttitre} <input name="formtitre" id="formtitre" type="text" size="40" maxlength="80" value=""></td><td> </td></tr>
<tr><td align="right">{$loctxtfav} <input name="formlink" id="formlink" type="text" size="40" maxlength="400" value=""></td><td> </td></tr>
<tr><td align="right">{$loctxtcomm} <input name="formcomm" id="formcomm" type="text" size="40" maxlength="80" value=""></td><td> </td></tr>
<tr><td align="right">{$loctxtclefs} <input name="formkeyw1" id="formkeyw1" type="text" size="10" maxlength="20" value=""> <input name="formkeyw2" id="formkeyw2" type="text" size="10" maxlength="20" value=""> <input name="formkeyw3" id="formkeyw3" type="text" size="10" maxlength="20" value=""></td><td> </td></tr>
<tr><td colspan="2" align="right"><a href="javascript:appliquer(true);"><img src="$DelFavIcon" id="deleteButton" style="visibility: hidden;" border="0" alt="D"></a> <a href="javascript:appliquer(false);"><img src="$courant/images/icon_ok.png" border="0" alt="V"></a></td></tr>
</tbody></table>
</form>
</div>
This is the file referenced above
This time, the PHP script outputs formatted data with a separator, that I split on the receiver side.
<?php
//
// getFavorite.php
//
//VGR13032006 Création pour Q ajax
//VGR18032006 FIXed missing CRITICAL log level
//VGR28032006 FIXed addslashes() problem
//VGR05042006 FIXed problème d'id_user=0,1 pour l'admin (SBL)
//
// REM reçoit id= en GET
//
// TODO : Nil
//
if ( isset($_GET['id']) ) {
require_once('header.inc.php');
$query="select * from $dbTableFavoris where id={$_GET['id']};";
$result=mysql_query($query,$linkId) or die("bad query '$query' : ".mysql_error());
if ($res=mysql_fetch_array($result)) {
if ( ($res['id_user']==$_SESSION['sess_id'])OR($_SESSION['sess_admin']>0) ) { // ok
echo $res['favori'].'|'.stripslashes($res['titre']).'|'.stripslashes($res['commentaire']).'|'.stripslashes($res['motsclefs']);
} else LogAction("Erreur Critique : utilisateur {$_SESSION['sess_id']} essaie d'effacer un favori {$_GET['id']} ne lui appartenant pas!",CRITICAL);
} else LogAction("Erreur Critique : utilisateur {$_SESSION['sess_id']} essaie d'effacer un favori {$_GET['id']} inexistant!",CRITICAL);
} else echo "appel incorrect.";
?>
Here is a complete set of working files (that is, TWO files, the strict minimum minimorum) that you can see in action here.
Client side : AjaxDemo.php
<?php
//
// AjaxDemo.php : AJAX Demonstration Page
//
//VGR03092006 Creation
//
// TODO : Nil
//
echo <<<EOHEAD
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head>
<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
<meta name="GENERATOR" content="Notepad">
<title>AJAX Demo</title>
<!-- this AJAX definition is usually best placed in HEAD-->
<script language="JavaScript" type="text/javascript" src="ajax.js"></script>
EOHEAD;
echo <<<EOHTML
</head>
<body>
<!-- this is the User Interface on which I "needed" AJAX to go to the server asynchronously-->
<form action="" method="POST" OnSubmit="return false;">
Input some ID : <input type="text" name="id" id="id_id" value="" OnChange="EditFunc();"> Dummy field to trigger OnChange : <input type="text" name="whatever" value="">
</form>
<hr>
<!-- this is not necessary ; for Demo purposes, it displays the AJAX answer.-->
<!--the style should normally be in the CSS file-->
AJAX Response : <div id="ajaxMessage" style="color:red;"></div>
<!-- this AJAX handling part should be in BODY, AFTER the referenced HTML elements-->
<script language="JavaScript" type="text/javascript">
function showUpdate() {
document.getElementById('ajaxMessage').innerHTML = ajax.response;
}
var EditObj=document.getElementById('id_id');
function EditFunc() {
var id=EditObj.value; if (id.length==0) id=null; // (1)
var transmit_only_not_null=(id!=null)?"?id="+id:""; // (1)
// get data via AJAX ;-)
ajax.requestFile = 'getDummy.php' + transmit_only_not_null; // (1)
ajax.onCompletion = showUpdate; // Specify function that will be executed after file has been found
ajax.runAJAX();// Execute AJAX function
}
</script>
</body>
</html>
EOHTML;
?>
(1) For Demo purposes, I twisted the rather classical line ajax.requestFile = 'getDummy.php?id='+id;
so that the id= parameter doesn't get transmitted via GET when no value is provided, to effectively trigger the "KO incorrect call" case on the server.
Server side : this is getDummy.php, the file referenced above in the AJAX call
As you can see, you can play with entering '' (empty, void, null, 0-length string as you like), ' ' (spaces), 'qsd' ( a string value), 0 (zero) and non-zero numbers/scalars. The server will answer accordingly.
<?php
//
// getDummy.php
//
//VGR03092006 Creation for AJAX demonstration
//
// REM reçoit id= en GET (1) MAY receive
//
// TODO : Nil
//
if ( isset($_GET['id']) ) {
if (is_numeric($_GET['id'])) {
if ($_GET['id']>0) {
echo "OK, received ID='{$_GET['id']}'";
} else echo "KO, received ID=0";
} else echo "KO, received non-scalar value '{$_GET['id']}'";
} else echo "KO, incorrect call";
?>
This technique is extremely simple and efficient and does the job well. You don't have to dig into unnecessary details (remember Occam's Razor).
Just include the correct .js file, drop the few necessary lines of JavaScript to your generated HTML pages, create one receiver script on the server, and you're done.
Copyright(©)(c) the respective authors. Licensed under the Creative Commons Attribution Share Alike 3.0 License .
These pages are served without commercial sponsorship. (No popup ads, etc...). Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE.
Please DO link to this page!