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

4

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:

 

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:

 

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) {<br />  var parent = el.parentNode;<br />  if (parent)<br />    if (parent.tagName ==tagName) <br />      return parent;<br />    else<br />      return findParentWithTag(parent, tagName);<br />  else<br />    return null;<br />}//findParentWithTag <br />

 

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

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

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(&quot;div&quot;,  &quot;result = div.id.indexOf('VerplichtingDatum'); return result &amp;gt; -1;&quot;);<br />  divs = findChildrenWithTagAndCondition( tr, &quot;DIV&quot;, evaldiv);<br />&nbsp;

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}&nbsp;

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) {<br />  // hide the unveil icon<br />  icon.className='invisible';<br />  // show the hide (again) icon<br />  el = document.getElementById(icon.id.replace(&quot;show&quot;,&quot;hide&quot;));<br />  // set its classname to null<br />  el.className='';<br /> <br />  // find and unveil DateFields<br />  //   find parent TR<br />  tr = findParentWithTag(icon, &quot;TR&quot;);<br />  var evaldiv = new Function(&quot;div&quot;,  &quot;result = div.id.indexOf('VerplichtingDatum'); return result &amp;gt; -1;&quot;);<br />  divs = findChildrenWithTagAndCondition( tr, &quot;DIV&quot;, evaldiv);<br />  for( var i = 0; i &amp;lt;  divs.length; i++ ) {<br />    divs[i].className=''; <br />  }<br />}<br /><br /> 

Share.

About Author

Lucas Jellema, active in IT (and with Oracle) since 1994. Oracle ACE Director for Fusion Middleware. Consultant, trainer and instructor on diverse areas including Oracle Database (SQL & PLSQL), Service Oriented Architecture, BPM, ADF, Java in various shapes and forms and many other things. Author of the Oracle Press book: Oracle SOA Suite 11g Handbook. Frequent presenter on conferences such as JavaOne, Oracle OpenWorld, ODTUG Kaleidoscope, Devoxx and OBUG. Presenter for Oracle University Celebrity specials.

4 Comments

  1. Hi Lucas,
    about findChildrenWithTagAndCondition():
    1) all “” have a bad editing, so appear as “>” and so on.
    2) the function seems to allow the parameter tagName as optional:
    if(tagname) do this,
    else do that
    but in else case, the function still wants tagname few lines after.

    did I miss something ?

  2. Jeroen,

    Thanks for the tip! I will go and rewrite my code using JQuery. Better do this rightaway before I become attached to my own code. And I like the speed promised with JQuery! (and the fact that it is code that has been tested).

    Lucas

  3. Jeroen van Wilgenburg on

    Have you looked into jQuery? I’m afraid you rewrote some of the jQuery functions.

    Code sample 1 (http://docs.jquery.com/DOM/Traversing):
    Find an Element’s Ancestor of a specific type (tag):
    $(“span”).parents(“p”)
    Find all parent elements of each span that is a paragraph.

    For the second sample you can use
    find(expr) (though I’m not sure about that, but I think it can do what you want)

    For showing and hiding you can use show() and hide():
    $(“p”).show() will hide all ‘s

    jQuery is a really great Javascript library and is only 20KB. Ajaxian recently pointed to an article to show that jQuery also is one of the fastests libraries out there:
    http://ajaxian.com/archives/jquery-113-800-faster-still-20kb
    It’s usually more efficient to write your own methods (you only write what you need), but I think it’s very hard to write faster code than jQuery.

    But still a very nice article, seems I’m not the only Mr. Dojo at AMIS ;-)