Cross-Platform DHTML
by Charlie Ma

Listing One
// in html examples this bit of code is referred to as file listing_1.js

// Detect browser version
var isNav5 = false;
var isNav4 = false;
var isNav = false;
var isIE4 = false;
var isIE5 = false;
var isIE = false;
var isWin = false;
var isMac = false;

if(navigator.appName=="Netscape") {
    isNav=true;
    if(parseInt(navigator.appVersion) == 4) isNav4=true;
    if(parseInt(navigator.appVersion) == 5) isNav5=true;
}
else if(navigator.appName=="Microsoft Internet Explorer") {
    isIE = true;
    if(navigator.appVersion.indexOf("MSIE 4") != -1) isIE4=true;
    if(navigator.appVersion.indexOf("MSIE 5") != -1) isIE5=true;
}
// Detect OS platform
if (navigator.platform.indexOf("Win") != -1 ) isWin = true;
if (navigator.platform.indexOf("Mac") != -1) isMac = true;


Listing Two
// in html examples this bit of code is referred to as file listing_2.js

// This function recursively steps through the node heirarchy
// and puts the structure in msg.
function getChildObjects(obj, formatIndent) {
    var msg = '';
    if (formatIndent) {
        msg = formatIndent;
    }

   if (obj.nodeType == 1) {
        // nodeType==1 for all HTML tags other than comments and CDATA.
                msg += "&lt;" + obj.nodeName;
        for (var j=0; j<obj.attributes.length; j++) {
            if (obj.attributes.item(j).nodeValue) {
               msg += " " + obj.attributes.item(j).nodeName
                   +  "='" + obj.attributes[j].nodeValue + "'";
            }
        }
        msg += "&gt;";
    }
    else {
        // W3C's DOM1 spec provides other nodeTypes for text, CDATA,
entity
        // reference, etc.  Not all of these are implemented by the
browsers.
        msg += obj.nodeName;
    }
    msg += " nodeType: " + obj.nodeType + "\n";
    for (var i=0; i<obj.childNodes.length; i++) {
        nextIndent = prettyIndent(obj, formatIndent);
        msg += getChildObjects(obj.childNodes[i], nextIndent);
    }
    return msg;
}
// does nothing other than manage the indents for output
function prettyIndent(obj, formatIndent) {
    if (formatIndent && formatIndent.length>3 && obj.parentNode) {
        nextIndent = formatIndent.substring(0, formatIndent.length-4);
        nextIndent += (obj == obj.parentNode.lastChild)?
                                            '      +-- ' : '|     +-- '
;
    }
//  uncomment to fix problem caused by <HTML> node not returning a
parentNode.
//  else if (isIE5 && obj.nodeName == 'HTML') {
//      nextIndent = '         +-- ';
//  }
    else {
        nextIndent = '   +-- ';
    }
    return nextIndent;
}
// calls getChildObjects and writes the returned msg to the
// text area of a new html page.
function showDOM(domObjId) {
    var obj = (domObjId)? document.getElementById(domObjId) : document;
    var msg = getChildObjects(obj);
    newHTML = "<HTML><BODY><form><textarea rows=30 cols=120'>" + msg +
                                     "</textarea></form></BODY></HTML>"
    document.write(newHTML);
}


Listing Three
// in html examples this bit of code is referred to as file listing_3.js

// taks a layer id and recursively steps through Nav4's
// layer heirarchy.  Returns the layer with the specified id.
function getNav4Layer(layerId, parent) {
    var objLayer;
    var parentObj = (parent)? parent : document;
    for (var i=0; i<parentObj.layers.length && !objLayer; i++) {
        if(parentObj.layers[i].id == layerId) {
            objLayer = parentObj.layers[i];
        }
        else {
            objLayer = getNav4Layer(layerId, parentObj.layers[i]);
        }
    }
    return objLayer;
}
// object constructor for CSSObject.
function CSSObject(obj)  {
    if (isIE5 || isNav5)  {
        this.name = obj;
        this.elem = document.getElementById(obj);
        this.css = this.elem.style;
    }
    else if(isNav4)  {
        this.name = obj;
        this.elem = getNav4Layer(obj, 0);
        this.css = this.elem;
    }
    else if(isIE4)  {
        this.name = obj;
        this.elem = document.all[obj];
        this.css = this.elem.style;
    }
}
// ----------- defines the moveBy method of CSSObject
-----------------------
function moveByNav(x, y) {
    this.css.left = parseInt(this.css.left) + x;
    this.css.top = parseInt(this.css.top) + y;
}
function moveByIE(x, y) {
    this.css.pixelLeft += x;
    this.css.pixelTop += y;
}
if (isNav4 || isNav5)       CSSObject.prototype.moveBy = moveByNav
else if (isIE4 || isIE5)    CSSObject.prototype.moveBy = moveByIE;
// ----------- defines the moveTo method of CSSObject
-----------------------
function moveToNav(x, y) {
    this.css.left = x;
    this.css.top = y;
}
function moveToIE(x, y) {
    this.css.pixelLeft = x;
    this.css.pixelTop = y;
}
if (isNav4 || isNav5)       CSSObject.prototype.moveTo = moveToNav
else if (isIE4 || isIE5)    CSSObject.prototype.moveTo = moveToIE;
// ----------- defines the write method of CSSObject
-----------------------
// What follows only works with IE4 on Win (for css objects defined with
<div>
// and not <span>), IE5 on both Win and Mac, Nav4, and Nav5.
function HTMLWriteNav4(html) {
    this.css.document.open();
    this.css.document.write(html);
    this.css.document.close();
}
function HTMLWriteIE5(html) {
    this.elem.innerHTML = html;
}
function HTMLWriteNav5(html) {
    var rng = document.createRange();
    rng.selectNodeContents(this.elem);
    rng.deleteContents();
    var htmlFrag = rng.createContextualFragment(html);
    this.elem.appendChild(htmlFrag);
}
if (isNav4)         CSSObject.prototype.write = HTMLWriteNav4
else if (isNav5)    CSSObject.prototype.write = HTMLWriteNav5
else if (isIE5 || (isIE4 && isWin))
                    CSSObject.prototype.write = HTMLWriteIE5;
Listing Four
// in html examples this bit of code is referred to as file listing_4.js

// returns document's image object of given name.
// For Nav4 getImgObjNav4 is called
function getImageByName(imgName) {
    if (isIE4 || isIE5 || isNav5) {
        return document.images[imgName];
    }
    else if (isNav4) {
        return getImgObjNav4(imgName);
    }
}
// returns document's form object of given name.
// For Nav4 getFormObjNav4 is called
function getFormByName(formName) {
    if (isIE4 || isIE5 || isNav5) {
        return document.forms[formName];
    }
    else if (isNav4) {
        return getFormObjNav4(formName);
    }
}
// steps throug Nav4's layer heirarchy recursively
// to get the image object with given name
function getImgObjNav4(imgName, parent) {
    var objImage;
    var parentObj = (parent)? parent : document;
    objImage = parentObj.images[imgName];
    if (!objImage) {;
        for (var i=0; i<parentObj.layers.length && !objImage; i++) {
            objImage = getImgObjNav4(imgName,
parentObj.layers[i].document);
        }
    }
    return objImage;
}
// steps throug Nav4's layer heirarchy recursively
// to get the form object with given name
function getFormObjNav4(formName, parent) {
    var objForm;
    var parentObj = (parent)? parent : document;
    objForm = parentObj.forms[formName];
    if (!objForm) {;
        for (var i=0; i<parentObj.layers.length && !objForm; i++) {
            objForm = getFormObjNav4(formName,
parentObj.layers[i].document);
        }
    }
    return objForm;
}


Example 1:

(a)
undefined nodeType: undefined
   +-- <HTML> nodeType: 1
   +-- <HEAD> nodeType: 1
   |     +-- <TITLE> nodeType: 1
   |     +-- <STYLE> nodeType: 1
   |     +-- <SCRIPT id='listing_1' src='listing_1.js'> nodeType: 1
   |     +-- <SCRIPT id='listing_2' src='listing_2.js'> nodeType: 1
   +-- <BODY> nodeType: 1
         +-- <DIV id='L1'> nodeType: 1
         |     +-- <IMG name='Img1' src='Image1.gif'
         |                               start='fileopen'> nodeType: 1
         |     +-- #text nodeType: 3
         +-- <IMG align='top' name='Img2' src='Image2.gif'
         |                                start='fileopen'> nodeType: 1
         +-- #text nodeType: 3
         +-- <BR> nodeType: 1
         +-- <!> nodeType: 1
         +-- <FORM encType='application/x-www-form-urlencoded'
         |      id='form1ID' method='get' name='form1Name'> nodeType: 1
         |     +-- <TEXTAREA id='txtarea' wrap='soft'> nodeType: 1
         |           +-- #text nodeType: 3
         +-- <BR> nodeType: 1
         +-- <A href='javascript:showDOM()'> nodeType: 1
         |     +-- #text nodeType: 3
         +-- #text nodeType: 3


(b)
#document nodeType: 9
   +-- <HTML> nodeType: 1
         +-- <HEAD> nodeType: 1
         |     +-- #text nodeType: 3
         |     +-- <STYLE> nodeType: 1
         |     |     +-- #text nodeType: 3
         |     +-- #text nodeType: 3
         |     +-- <SCRIPT id='listing_1' src='listing_1.js'> nodeType:
1
         |     +-- #text nodeType: 3
         |     +-- <SCRIPT id='listing_2' src='listing_2.js'> nodeType:
1
         +-- #text nodeType: 3
         +-- <BODY> nodeType: 1
               +-- #text nodeType: 3
               +-- <DIV id='L1'> nodeType: 1
               |     +-- #text nodeType: 3
               |     +-- <IMG name='Img1' src='Image1.gif'> nodeType: 1
               |     +-- #text nodeType: 3
               +-- #text nodeType: 3
               +-- <IMG name='Img2' src='Image2.gif' align='Top'>
nodeType: 1
               +-- #text nodeType: 3
               +-- <BR> nodeType: 1
               +-- #text nodeType: 3
               +-- #comment nodeType: 8
               +-- #text nodeType: 3
               +-- <FORM name='form1Name' id='form1ID'> nodeType: 1
               |     +-- <TEXTAREA defaultvalue='ha ha ha'
               |                  value='ha ha ha' id='txtarea'>
nodeType: 1
               +-- <BR> nodeType: 1
               +-- #text nodeType: 3
               +-- <A href='javascript:showDOM()'> nodeType: 1
               |     +-- #text nodeType: 3
               +-- #text nodeType: 3


Example 2:
if (isNav4) document.layers['L1'].left = 10
else if (isIE4) document.all['L1'].style.pixelLeft = 10
else if (isNav5) document.getElementById('L1').style.left = 10
else if (isIE5) document.getElementById('L1').style.pixelLeft = 10;


