AJAX is becoming a popular buzzword. Prototyping is something I am just getting into, but for the most part I copy paste/adapt my personal js library functions in each new project I start (as well as using free open sourced libraries from the internet). Here is an example of some of my javascript in action.
// BA.js
// modules for BA.aspx
// mhogg jan07
//---------------------
// Functions-----------
// showSearch() three show() functions
// showEmployee() ^
// showDependent(depID, EFID) ^
// removeNavPath() show() helper
// addResultsLink() show() helper
// setTitle(titleVal) show() helper
// showDiv(divID) show() helper
// LoadXMLDoc(url) requests employee data from server (1)
// blinkProgress() helper- blinks cute yellow box (2)
// BuildXMLResults() catches response (3)
// setData(rX) does something with data I forget (4) calls next two functions
// setPeriodLinks() creates date links on left side of page (4a)
// fillDetails(instanceNum) the meat of the page- uses two helper functions that follow this one (4b)
// clearData() helper- clears any values left from previous Instance
// findOlderVal(pRoot, arrayNum) helper- sets most recent value for a field in case of null value; always black
//---------------------
// Global vars
var navPath; //initialized in showSearch()
var tid;// used for Progress BlinkBox
var empXml; // this holds the Dataset Tables XML for one Employee
var empName,empID; //set in fillDetails(), used in show() functions
// the next two vars need to match each other for processing each XML value to a form field (span)
var fieldArray = new Array('EIGname','EIGempid','EIGaddress','EIGcity'…
var valArray = new Array('NAME','EID','ADDRESS','CITYANDZIP','HOME_PHONE'…
//--------------------
// Bodies
function showSearch() {
navPath = document.getElementById('divNavPath');//first time initialization/ used in other functions
showDiv('divFilterAndResultsList');
setTitle('Search Results');
empXml = null;
setPeriodLinks();//indirectly clears period links
removeNavPath();
navPath.appendChild(document.createTextNode('> Search Results '));
}
…
function showDependent(depID, EFID) {
showDiv('divDependentInstance');
//set values for each span
//js xpath??
if ( document.evaluate ) { // W3C implemenation; else IE implementation
var xPathExp = "/NewDataSet/Table1[EFID=" + EFID + " and DEPID=" + depID + "]/DNAME";
var xName = document.evaluate(xPathExp, empXml, null, XPathResult.ANY_TYPE, null );
depName = xName.singleNodeValue.textContent;
}else depName = empXml.selectNodes("/NewDataSet/Table1[EFID=" + EFID + " and DEPID=" + depID + "]/DNAME")[0].firstChild.nodeValue ;
removeNavPath();
addResultsLink();
node = document.createElement('a');
node.href = 'javascript:showEmployee();';
node.appendChild(document.createTextNode(empName));
navPath.appendChild(document.createTextNode(' > '));
navPath.appendChild(node);
navPath.appendChild(document.createTextNode(' > ' + depName));
setTitle(depName);
//setPeriodInstances for dependent
}
So there was creating DOM manipulation, note the js XPath implementation that was actually easier in IE back then. That code breaks today since IE has become more compliant. Here’s my ajax:
//-----------------------
function LoadXMLDoc(url){
if (window.XMLHttpRequest){ //Mozilla, Firefox, Opera 8.01, Safari, and now IE7?
reqXML = new XMLHttpRequest();
reqXML.onreadystatechange = BuildXMLResults;
reqXML.open("POST", url, true);
reqXML.send(null);
}
else if(window.ActiveXObject){ // old IE
reqXML = new ActiveXObject("Microsoft.XMLHTTP");
if (reqXML) {
reqXML.onreadystatechange = BuildXMLResults;
reqXML.open("POST", url, true);
reqXML.send();
}
}
else{ //Older Browsers
alert("Your Browser does not support XMLHttp!");
}
blinkProgress();
}
function blinkProgress() {
document.getElementById('inProgress').style.left = (document.body.clientWidth - 300) / 2;
if (document.getElementById('inProgress').style.display=="none") {
document.getElementById('inProgress').style.display="";
} else document.getElementById('inProgress').style.display="none";
tid = setTimeout('blinkProgress()', 500);
}
function BuildXMLResults(){ if(reqXML.readyState == 4){ //completed state clearTimeout(tid); document.getElementById('inProgress').style.display="none"; if(reqXML.status == 200){ //We got a success page back if(reqXML.responseXML.documentElement && reqXML.responseXML.documentElement.getElementsByTagName('TABLE') != null){ //dummy test //window.status = reqXML.responseXML; //display the message in the status bar setData(reqXML.responseXML.documentElement); } else{ //Something's not right alert("There was a problem retrieving the XML data:\nMissing ID; " + reqXML.responseText); } } else{ //display server code not be accessed alert("There was a problem retrieving the XML data:\nMissing 200; " + reqXML.responseText); } }}
We wrote our own progress bars back then, and animations, like here:
// the search Results page slides up or down
function toggleDisplay() {
//alert('display'); //debug
// match gets an array, the second value ([1]) is what we are looking for- the value of display
var cV = document.cookie.match ( 'display=(.*?)(;|$)' );
if ( (cV) && cV[1] == "down" ) {
document.cookie = "display=up";
move("up");
document.getElementById('ddlCsearch').style.display = '';
document.getElementById('lbIsearch').style.display = '';
document.getElementById('tblSearch').style.width = '800px';
} else {
document.cookie = "display=down";
move("down");
document.getElementById('ddlCsearch').style.display = 'none';//ie6
document.getElementById('lbIsearch').style.display = 'none';//ie6
document.getElementById('tblSearch').style.width = '300px';
}
}
function move(direction){
//alert('#2: move' + document.getElementById('pnlResults').style.top); //debug
var currTop = document.getElementById('pnlResults').style.top.substr(0, document.getElementById('pnlResults').style.top.length - 2);
if (direction == 'down') {
if (currTop <= 30) {
return;
}else {
document.getElementById('pnlResults').style.top = currTop - 30;
setTimeout('move("down")',20);
}
}else {
if (currTop >= 340) {
return;
}else {
document.getElementById('pnlResults').style.top = eval(currTop) + 30;
setTimeout('move("up")',20);
}
}
}
Also our own validation:
// ----- custom client side validation
function validate() {
var badFields = '';
var aTR = document.getElementsByTagName('TR');
var aRowChecker = 0;
var focusFlag = false;
for (var row=0;row<aTR.length;row++) { //for each table row in form
var cell = 0;
while (aTR[row].childNodes[cell]){ // for each table cell in form
var cellchild = 0;
while (aTR[row].childNodes[cell].childNodes[cellchild]) { // for each DOM obj in cell
var object = aTR[row].childNodes[cell].childNodes[cellchild];
if (object.tagName == 'SELECT'){
if (aTR[row].childNodes[0].innerHTML) { // IE
var myText = aTR[row].childNodes[0]
} else { var myText = aTR[row].childNodes[1]} //Mozilla
// Drop down validation
if (object.id.indexOf('result') > 0 &&
object.selectedIndex == 0) {
badFields += myText.innerHTML + '\n';
myText.style.color = 'red';
if (focusFlag == false){object.focus();focusFlag=true;}
} else {myText.style.color = 'black';}
}
if (object.tagName == 'INPUT') {
if (aTR[row].childNodes[0].innerHTML) { // IE
var myText = aTR[row].childNodes[0]
} else { var myText = aTR[row].childNodes[1]} //Mozilla
// textbox validation
if (object.getAttribute('type') == 'text' &&
object.value.length == 0 &&
object.id.indexOf('result') > 0 &&
object.maxLength < 200) { //comments not a required field
badFields += myText.innerHTML + '\n';
myText.style.color = 'red';
if (focusFlag == false){object.focus();focusFlag=true;}
} // Regex validation
else if ( object.getAttribute('type') == 'text' &&
( object.id.indexOf('result') > 0 || (object.id.indexOf('Q') == 0 && object.maxLength > 200) ) &&
!object.value.match(/^[-a-zA-Z,. @!()\''""?0-9]*$/)) {
alert('Please use normal characters in ' + myText.innerHTML + object.value);
object.focus();
myText.style.color = 'red';
return false;
} // Radio validation */
else if (object.getAttribute('type') == 'radio') {
var aRadio = document.getElementsByName(object.name);
if(aRowChecker != row){ // aRowChecker used so we don't prompt user five times for each missed question
if (myText.firstChild.nodeType == 1) {myText=myText.firstChild;} // in case -style.color=red- applied, just get text
if (!aRadio[0].checked && !aRadio[1].checked && !aRadio[2].checked && !aRadio[3].checked && !aRadio[4].checked){
badFields += myText.innerHTML + '\n';
myText.style.color = 'red';
aRowChecker=row;
if (focusFlag == false){object.focus();focusFlag=true;}
}
else {myText.style.color = 'black';}
}
} else {myText.style.color = 'black';}
}
cellchild++;
}
cell++;
}
}
if (badFields.length > 0) {
alert('Please fill in the following fields:\n' + badFields);
return false;
}
else return true;
}
My approach for browser compliance was to work in the major or main four browsers by using the simplest code possible. I avoided hacks if I could.