Generic JavaScript functions for generic DOM exploration – leveraging the JavaScript dyamic function creation

Often there is a need for client side (JavaScript) DOM oriented manipulation in Web Applications. To achieve a higher degree of user interactivity, more productivity or simply a more visually appealing user interface. And such client side operations come almost invariably with a lot of DOM manipulation – direct interaction with the nodes of the browser in memory representation of the HTML document. This blog discusses an example of such manipulation and introduces a little generic code that may be of use to many people

The example I have to work on is shown below:

Generic JavaScript functions for generic DOM exploration - leveraging the JavaScript dyamic function creation 

In this entry form, the user can enter two sorts of values: amounts (in euros) and percentage for each quarter in the current year. Occasionally, the user must also enter a specific date value. However, since that is relatively rare and this matrix can easily contain dozens of rows, it is deemed better to only show those date fields when they are actually needed.

Toggling the view – aka client side detail disclosure – should unveil the date fields in the table row in which the disclose icon was clicked. The icon should change its appearance into a close date fields icon. Something like:

Generic JavaScript functions for generic DOM exploration - leveraging the JavaScript dyamic function creation 

The main requirements in terms of DOM interaction....
:

  • from the toggle icon, find its enclosing TR (table row) ancestor (since the disclose acts on all fields in the same row)
  • from the Table Row, find all descendants that are a SPAN and satisfy a certain naming condition
  • hide the disclose icon/show the close icon; unveil the date fields (and later on hide them again)

The third requirement – hiding and displaying elements – is one I have solved many times, so that’s not the issue. The other two are not really much of an issue, except for the fact that I do not do this sort of thing often enough, so I keep reinventing the same wheel. Now for my own benefit – in order to find this code when next I have a need for it, in six months time or so – the more or less generic functions that help me implement these two requirements:

Find an Element’s Ancestor of a specific type (tag):

function findParentWithTag(el, tagName) {
var parent = el.parentNode;
if (parent)
if (parent.tagName ==tagName)
return parent;
else
return findParentWithTag(parent, tagName);
else
return null;
}//findParentWithTag

 

Find all descendants – at all levels in the tree – for a given element that satisfy the given condition

function findChildrenWithTagAndCondition( el, tagName, evalfunction) {
var children = new Array();
if (tagName) {
descendants = el.getElementsByTagName(tagName);
for (var j =0; j < descendants.length;j++) {
if ( evalfunction(descendants[j]))
{ children.push(descendants[j]);
} //if
} //for
}
else {
for( var i = 0; i < el.childNodes.length; i++ ) {
if (el.childNodes[i].tagName==tagName ) {
if ( evalfunction(el.childNodes[i]))
{ children.push(el.childNodes[i]);
} //if
} //if
// investigate the children of this element
descendants = findChildrenWithTagAndCondition( el.childNodes[i], tagName, evalfunction);
if (descendants && descendants.length > 0) {
// add all children found (including descendants further down the tree) to the list of qualifying children
for (var j =0; j < descendants.length;j++) {
children.push( descendants[j]);
} //for
}//if
}//for
}
return children;
}// findChildrenWithTagAndCondition

This latter function can be called with the element itself, an optional tag name and the function that should be called to find out whether or not a certain child element qualifies. Here we can use the fun characteristic of JavaScript that allows us to create functions on the fly.

  var evaldiv = new Function("div",  "result = div.id.indexOf('VerplichtingDatum'); return result > -1;");
divs = findChildrenWithTagAndCondition( tr, "DIV", evaldiv);
 

This example shows the creation of an evaluation function that has a single input parameter (called div) and executes the evaluation "div.id.indexOf(‘VerplichtingDatum’)" : all div children whose id attribute contain the string VerplichtingDatum qualify.

Hide/Display elements dynamically 

To make the story complete – even though this is most trivial part – I will show how to hide/display element through DOM Manipulation.

First of all, I have included the following CSS style in my stylesheet:

.invisible {display:none} 

Next, to hide an element, I simply apply this style to it. To make it visible, I set the style to nothing at all. For example, to unveil the Date Fields that are contained in DIV element whose styleClass is initially set to invisible:

function showDateFields(icon) {
// hide the unveil icon
icon.className='invisible';
// show the hide (again) icon
el = document.getElementById(icon.id.replace("show","hide"));
// set its classname to null
el.className='';

// find and unveil DateFields
// find parent TR
tr = findParentWithTag(icon, "TR");
var evaldiv = new Function("div", "result = div.id.indexOf('VerplichtingDatum'); return result > -1;");
divs = findChildrenWithTagAndCondition( tr, "DIV", evaldiv);
for( var i = 0; i < divs.length; i++ ) {
divs[i].className='';
}
}

4 Comments

  1. Andrea September 25, 2007
  2. Andrea September 25, 2007
  3. Lucas Jellema July 5, 2007
  4. Jeroen van Wilgenburg July 5, 2007