Howardism Musings from my Awakening Dementia
My collected thoughts flamed by hubris
Home PageSend Comment

Dynamic Content with Javascript

I'll be the first to admit that Javascript is quite painful when dealing with it. The only reason why we use it, is because it is native to most web browsers, however its most painful feature is that each browser supports it differently. But Javascript is still the best way to make a web app behave similarly to its cousin, the native client app.

In trying to make a particular dialog expand and contract with new fields based on selections by the user, I came up with a series of notes and lessons on making Javascript work on the latest crop of browsers, i.e. Firefox/Mozilla, IE, Opera and Safari.

Don't check Javascript Version

Once upon a time, we would start to deal with browser incompatibility by specifying the version of Javascript that should be used for any given script section. Don't do that, as it isn't accurate.

Instead always use the following script tag:

<script type="text/javascript"> . . . </script>

And then in your code, do an if on the object and method before you do anything. For instance:

if ( obj && obj.method )
   obj.method();

Use `getElementById`

When hunting through a DOM (Document Object Model) of an HTML tree, the best approach is to label particular elements with an "ID" attribute. Then you can quickly grab that element with a call to getElementById(), and this approach works the same on all browsers.

Another option, getElementsByNames() is only supported by IE, and getElementsByTagNames() (which works on all browsers), gives you a collection of nodes, which you would then have to analyze or grab what you want. For instance, the following code grabs all of the <input> tags, and then hides any buttons it encounters:

var inputs = newCell.getElementsByTagName("input");
for (var i=0; i<inputs.length; i++)
{
    var input = inputs[i];
    if (input.type=="button" || input.type=="submit")
        input.style.display = "none";
}

Try to be Relative

Don't get crazy with hard-coding element references, for as soon as you do, the HTML will change, and then you've got to find all of the references in your Javascript. One approach is to walk the tree from a known starting point.

For instance, if the click of a button affects the "row" the button is on, as in:

<input type="button" 
    onclick="addService(this)" . . .>

Then walk this way to find the row that contains it:

function addService (button)
{
    var row=button.parentNode;
    while (row.nodeName!="TR") {
        row=row.parentNode;
    }
    . . .

Don't use InnerHTML

The innerHTML method seems like a wonderful way of adding dynamic content to things. The idea is that you could add a slew of buttons to a cell by doing something like:

var cell = document.getElementById("button-holder");
cell.innerHTML = '<input type="button" . . .';

However, while all the recent browsers support this method, IE does not accept code in the string that changes the layout. Yes, it is quite frustrating to create a dynamic form on Mozilla using the easy-to-use, innerHTML function, and have it completely die on IE.

Don't despair, if there is a complex table that you want to insert into your form, there is a solution…

Use Template Code

I found it better to create snippets of code to use as "templates." Each of these were marked as "hidden", and then I would clone the node containing the code to be copied, and then appended it to its new parent location. Example:

<table style="display: none;">
  <tbody  id="template-4">
    <tr style="padding-top: 12px;">
      <td>
        <textarea name="targetExp" cols="65" 
          rows="6" wrap="soft"></textarea>
      </td>
    </tr>
  </tbody>
</table>

Notice that the <table> tag has a style which sets the display to "none" (which hides its display. I then associate an ID with the subelement that I will be cloning with this Javascript code:

var template=document.getElementById('template-4');
var newnode = template.cloneNode(true);
newnode.id = "";

// Set the embedded <textarea> with a  default value
newnode.getElementsByTagName("textarea")[0].value=te;

formstart.appendChild(newnode);

Keep in mind that once you clone it, you need to change the ID, as the cloned node will inherit it on IE.

What `TBODY`?

The DOM implementation in IE assumes that all tables have a <tbody> tag… even if it isn't explicitly written in the HTML code. The best approach is to actually put it in the HTML code so that Mozilla and friends will act the same way as IE.

Use `deleteRow()`

I found the best approach to building a dynamic, changeable form is to make heavy use of <table>s for holding everything. This means that you will be deleting rows first, and then adding new rows in order to change the content of certain sections.

The easiest way to delete a row is to use the deleteRow() method on a table element. It takes a parameter of the index of the row to delete. If you have a pointer to the row element, this can be done with this code:

try {
    table.deleteRow(row.rowIndex);
}
catch (e) {
    table.deleteRow(-1);
}

The catch code is there because of an exception that is sometimes thrown when accessing the row.rowIndex in Firefox. Since in my particular case, the user is often deleting the last row, I put a catch here and call deleteRow() with a -1 (to mean, delete the last row).

Turning Things On and Off

More often than adding new elements to a form, you will be hiding and displaying some fixed code (often based on the value of a checkbox or radio button). If the element to be toggled has an ID, then it is quite easy to do this:

<input type="radio" name="advform" value="simple"
  onclick="document.getElementById('adv').style.display='none'" />
    Simple
<input type="radio" name="advform" value="advanced"
  onclick="document.getElementById('adv').style.display=''" />
    Advanced
&hellip;
<td id="adv">
    &hellip;
</td>

Use a Library

The best way to deal with Javascript and the complexity associated with dealing with multiple browsers and multiple versions, is just to punt and use a Javascript library. With the advent of AJAX, there is a plethora to choose from.

My personal fav is Dojo Toolkit, as it does more than just AJAX, but contains a number of Javascript helper methods and abstractions.

Tell others about this article:
Click here to submit this page to Stumble It