Tuesday, May 30, 2006

Delete all children nodes

To remove all children nodes (from childNodes list) use:

while(listNode.firstChild) { // delete all nodes
       listNode.removeChild(listNode.firstChild);
}

Of course innerHTML will work too:

listNode.innerHTML = '';

Saturday, May 27, 2006

Traverse/walk DOM tree recursively

Task definition: You have a DOM tree (startNode which can be the whole document), and need to find first specific tag in this tree.

Here is the recursion function to do this:
function findNodeByTag(startNode, tagName) {
    if (startNode.nodeName.toLowerCase() == tagName.toLowerCase()) return startNode;
    var childList = startNode.childNodes;
    for (var i=0; i<childList.length; i++) {
        return findNodeByTag(childList[i], tagName);
    }
    return null;
}

And you call it:
findNodeByTag(myDOMobj, "img");

Add, delete and manipulate DOM nodes

Here are examples taken from some of my code just to memorize common use node functions. I hope they are self-explanatory.


// Clone DOM node
var movingUrl = urlLineSPAN.cloneNode(true);
// Get Parent node
var urlList = urlLineSPAN.parentNode;
// Delete DOM Node
urlList.removeChild(urlLineSPAN);
// Add DOM node
urlAreaObj.appendChild(newNode);
// or
urlAreaObj.insertBefore(newNode, existingNode);

Swap DOM nodes

To swap DOM nodes you can use this example:


var oldNodeAtPosition = document.getElementById(existingNode);
var newNodeAtPosition = document.getElementById(retrievedNode);
// swap DOM nodes
// API: replaceChild(child1,child2) -- replace node child2 by node child1
oldNodeAtPosition.parentNode.replaceChild(newNodeAtPosition.cloneNode(true), oldNodeAtPosition);
newNodeAtPosition.parentNode.replaceChild(oldNodeAtPosition.cloneNode(true), newNodeAtPosition);

Monday, May 15, 2006

DHTML: Dynamic Navigation through DOM element with scrollTop


I had a task to implement vertical navigation by the long list of items which does not fit the browser window. It has to be hidden if list of items shorter than height of the window. Note also I has to use frames.




Okey, let's start. First, I disabled standard scrollbars within the window:

<style>
html,body { overflow: hidden; }
body { position: absolute; top: 0; left: 0; }
</style>

Then I disabled scrollbars in DIV area which should be navigated:


#taglist{ width:100%; overflow:hidden; }

Then I created DIV area with up/down buttons bound to right lower corner of the window. I wrote in CSS file:


.sbnav { position: absolute; bottom: 5px; right: 5px; text-align:right; background-color: #fbffba; padding:3px; }
.sbnav-up { margin-bottom: 6px; }
.sbnav-down { margin-top: 6px; }

and in HTML (the simpliest piece):

<div id="taglist"></div>

Couple more tricks in this work:
1. Shift displayed area. For this purpose I have to control scrollTop DOM property. I wrote two functions for that: sbNavUp() and sbNavDown()
2. I found very useful: setInterval and clearInterval on mouse events (onmousedown and onmouseup).

Below is Javascript implementation. You will also need graphics for up and down buttons, and some HTML to define DOM object to be navigated, and DOM object which contain navigation.
NOTE that I had to put 110 and 115 values because area which should be navigatable is lower (shorter) than whole size of frame (window).

So, HTML for navigation arrows looks like this:

<div class="sbnav" id="sbnav">
<a href="#" id="sbnav_up" title="Up"><img src="/static/images/nav_up.gif" alt="Up" height="7" slass="sbnav-up" width="14" /></a><br />
<a href="#" id="sbnav_down" title="Down"><img src="/static/images/nav_down.gif" alt="Down" height="7" class="sbnav-down" width="14" /></a> </div>

Javascript calls on HTML page:
window.onload = sbNavControl;
window.onresize = sbNavControl;

Thus, I show or hide buttons on window resize.

Javascript implementation:

getWindowHeight function is available under the link.

/** Sidebar navigation */

var navStep = 2; // in pixels
var navSpeed = 10; // in milliseconds

/** Navigate list of interests up */

function sbNavUp() {
try {
var taglistObj = top.frames['sidebarcontent'].document.getElementById('taglist');
var offset = taglistObj.scrollTop;
if (offset > navStep) taglistObj.scrollTop -= navStep;
else if (offset <= navStep && offset > 0) taglistObj.scrollTop = 0;
} catch(e) {jsErr(e)}
}

/** Navigate list of interests down */

function sbNavDown() {
try {
var taglistObj = top.frames['sidebarcontent'].document.getElementById('taglist');
var offset = taglistObj.scrollTop;
if (offset+navStep < taglistObj.scrollHeight) taglistObj.scrollTop += navStep;
else if (offset+navStep >= taglistObj.scrollHeight && offset < taglistObj.scrollHeight) taglistObj.scrollTop = taglistObj.scrollHeight;
} catch(e) {jsErr(e)}
}

/** Show/hide navigational buttons */

function sbNavControl() {
try {
var taglistObj = top.frames['sidebarcontent'].document.getElementById('taglist');
var sbnabObj = top.frames['sidebarcontent'].document.getElementById("sbnav");
if (!taglistObj || !sbnabObj) return;
// reset height to default
taglistObj.style.height = '';
// display/hide nav buttons
var taglistHeight = taglistObj.offsetHeight;
if (taglistHeight > getWindowHeight()-110) sbnabObj.style.display = '';
else sbnabObj.style.display = 'none';
// set height in order to make navigation work
taglistObj.style.height = (getWindowHeight()-115) + "px";

// onMouseDown and onMouseUp events
var btnUp = top.frames['sidebarcontent'].document.getElementById("sbnav_up");
var btnDown = top.frames['sidebarcontent'].document.getElementById("sbnav_down");
if (!btnUp || !btnDown) return;
var sbNavUpInterval = ''; var sbNavDownInterval = '';
function sbNavUpStart() { sbNavUpInterval = self.setInterval("sbNavUp()", navSpeed); }
function sbNavDownStart() { sbNavDownInterval = self.setInterval("sbNavDown()", navSpeed); }
function sbNavUpStop() { self.clearInterval(sbNavUpInterval); }
function sbNavDownStop() { self.clearInterval(sbNavDownInterval); }
btnUp.onmousedown = sbNavUpStart;
btnUp.onmouseup = sbNavUpStop;
btnDown.onmousedown = sbNavDownStart;
btnDown.onmouseup = sbNavDownStop;
} catch(e) {}
}

DHTML: Add event dispatcher to HTML form field

Sure you can use this function with any DOM object. Not only a form field.


/* event dispatcher */
function installAC(frm, fld) {
a = document.forms[frm].elements[fld];
if (a.addEventListener) {a.addEventListener("keyup", PerformActions, false)} // firefox
else if (a.attachEvent) {a.attachEvent("onpropertychange", PerformActions)} // ie
}

/* actions to do */
function PerformActions(h) {
// your code here
}

DHTML: Get Height of window and DOM element

offsetHeight property of DOM element will let you get its height.

Following javascript function will return the height of the window:

/* Get window height. To get DOM element height use offsetHeight. */
function getWindowHeight() {
var windowHeight = 0;
if (typeof(window.innerHeight) == 'number') {
windowHeight = window.innerHeight;
} else {
if (document.documentElement && document.documentElement.clientHeight) {
windowHeight = document.documentElement.clientHeight;
} else {
if (document.body && document.body.clientHeight) {
windowHeight = document.body.clientHeight;
}
}
}
return windowHeight;
}