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
…
<td id="adv">
…
</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: