AJAX and Javascript libraries

by MikeHogg5. April 2007 09:44

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. 

About Mike Hogg

Mike Hogg is a c# developer in Brooklyn.

More Here

Favorite Books

This book had the most influence on my coding style. It drastically changed the way I write code and turned me on to test driven development even if I don't always use it. It made me write clearer, functional-style code using more principles such as DRY, encapsulation, single responsibility, and more.amazon.com

This book opened my eyes to a methodical and systematic approach to upgrading legacy codebases step by step. Incrementally transforming code blocks into testable code before making improvements. amazon.com

More Here