Super Dynamic Table Generator
Create a Table of Any Size with Any Contents on the Fly without Refreshing.

Article Purpose: For the reader to learn how to dynamically create tables of any size, with any contents, with any attributes, without refreshing the parent page. For this article, an example table generator is included. I've limited the minimum and maximum size of the generated table to avoid having to do a whole lot of work, but any programmer can use the source to make any table as small or as big as they like.

Details: This generator take the variables set by the user and creates an string of HTML that is then placed into the content window of an iframe. The iframe itself is also generated, but only so that the iframe frameBorder attribute can be turned off and on; otherwise, a static iframe tag can be used. The generator will also dynamically adjust the geometry of the iframe each time a table is generated. Below the generator is the generated iframe, below that is the generated HTML for the iframe, and below that is the thorough technical explaination of the generator code.

Table Generator Form
Number of Rows must be at least 2 and no greater than 32.
Number of Columns must be at least 2 and no greater than 32.
Turn on to see how the size of the iframe changes with the table size.
Turn on to see how the table is built.
Y is the delimiter, 1st number is Row, 2nd number is Column, use only one Y per X, skip no rows.

Key Legend
PNG32 Blank button, Zero state.
PNG32 Blank button, Hover state.
PNG32 Active State.

 

Generated HTML of the Table currently displayed in the IFRAME

 


Let's learn how to do this. I always use seperate .js files to hold my scripts. Download code_table_generator.js. in the head of the HTML page:

<script language=javascript src="code_table_generator.js"></script>

There's only one function that get's called, makeTable(); but some global variables need to be set first. These variables would only need to be global when doing other things in parallel with this function.

// initialize global variables
var outputString; //arrayed HTML to write that creates XY table
var rowValue; //number of rows from form
var colValue; //number of columns from form
var iframeBorder; //sets display of iframe border
var tableBorder; //sets display of table border
var yString  //preset status array from form
self.name='tablePage'; //isolates page from other pages

Start the function and set the local variables for the function. Note the variables that are being set to the value of the named form elements in the generator. Only the purpose of yString is not obvious; it sets which buttons have the red-dot in the table.

function makeTable() {
var rowValue=document.getElementById("formRows").value;
var colValue=document.getElementById("formColumns").value;
var iframeBorder=document.getElementById("formFrameBorder").checked;
var tableBorder=document.getElementById("formTableBorder").checked;
var yString=document.getElementById("formArray").value; 

Now we are going to parse the yString variable into an array. The new ioString variable is an array of continueous row/column (XY) coordinates. e.g., if yString equals Y1Y1Y2Y2Y3Y3 then what we're really saying is X1Y1, X2Y2, X3Y3. We put these numbers into an array called ioString, and get our values out by addressing the correct position in the array. e.g., ioString[0] returns 1, ioString[1] returns 1, and ioString[2] returns 2, and so on. Building up the array of on/off buttons doesn't have to be done this way, but it's a good example and illustrates how it might be useful in a "input to multiple outputs" industrial control.

var ioString=yString.split("Y");

In this generator, I validate the form options because I don't want to generate a 1x1 table or very large tables; however, this is completely optional.

switch (true) {
case (rowValue < 2):
alert('Row value must be at least 8');
return;
break
case (rowValue > 32):
alert('Row value must be no greater than 32');
return;
break
case (colValue < 2):
alert('Column value must be at least 8');
return;
break
case (colValue > 32):
alert('Column value must be no greater than 32');
return;
break
}

I also set some local variables according to the form inputs for deciding whether or not to show the frame border and table border; also, completely optional.

if (iframeBorder) {iframeBorder=1} else {iframeBorder=0}
if (tableBorder) {tableBorder=1} else {tableBorder=0}

I dynamically generate the IFRAME which is completely optional. It's a bit long, but it makes it easier for me to work with my code if everything is on one line; for this purpose, I have broken the sample code into multiple lines. This tag is only created so that the IFRAME frameBorder attribute can by shown or hidden. A static IFRAME tag works just as well, but then you won't be able to turn the frameborder on or off (and probably will never need to). For this dynamic bit of code, I use the innerHTML property of a DIV tag named "dynaFrame". I use innerHTML because using the "Document Object Model" (DOM) while probably being more proper is terribly time consuming.

document.getElementById("dynaFrame").innerHTML='<iframe name=\"dynaTable\" 
id=\"dynaTable\" width=\"1\" height=\"1\" frameborder=\"'+iframeBorder+'\" 
allowTransparency=\"true\" style=\"position:relative; z-index:1; padding:5px; 
overflow:auto\"></iframe>';

Now the fun starts. Here we start building our table output string. The very first time this string gets used, we set it only by "=", but every time after that, we will use the "+=" operator (the X=X+1 operation). This output string ultimately becomes the document within the IFRAME. The first thing we want to do is setup our opening HTML for the IFRAME.

outputString='<HTML>\r\n<head>\r\n<script language=javascript 
src=\"code_png_support_iframe.js\"></script>\r\n </head>\r\n<body 
STYLE=\"background-color:transparent\">\r\n<table align=\"center\" cellpadding=0 
cellspacing=1 border='+tableBorder+'>\r\n<tr><td rowspan='+(+rowValue+2)+'><center>
<img src=\"icon_outputs.png\" width=16 height=89 border=0></center></td>
<td colspan='+(+colValue+1)+'><center><img src=\"icon_inputs.png\" width=68 height=16 
border=0></center></td></tr>\r\n'

The code_png_support_iframe.js is nothing more than slightly modified canned Macromedia JavaScript. I like using it because it gives me excellent roll-over support, and my slight modifications allows for PNG alpha-channel transparency support in Internet Explorer 6. The rest of this string opens the HTML, HEAD, BODY, TABLE, and the first TR to set the table's rowspan and colspan attributes. Note the importance of placing a "+" infront of variables like rowValue; e.g. if rowValue was 2, the "+" turns the rowValue string into an integer, giving us "4" instead of "22". Also, this is where the "input" and "output" images I have created get included.


Now it's time to start row and column loops to create the contents of the table. For every row, we will cycle through all the columns, then go to the next row and repeat, again and again for every row in the table. First we create the row loop, and for each new row we must open the table row. Note how the outputString is now using the "+=" operator.

for(var i=0;i<rowValue;i++){
outputString+='<tr>'

We need to do things a little differently for the first rows and columns. In the first row, we want to number all the columns, except in the first two columns. This condition will check our current position in the row and column loops, and attach the correct strings to our variable.

if (i==0) {
for(var j=-1;j<colValue;j++){
if (j==-1) {
outputString+='<td width=21><center>&nbsp\;</center></td>';}
else {
outputString+='<td width=21><center>'+(j+1)+'</center></td>';
if (j+1==colValue){
outputString+='</tr>\r\n'
}
}
}
} 

We have to open each row. This condition is unlike the previous open row because that one won't work on the first two rows.

if (i==0) {
outputString+='<tr>'
} 

Now that we're looping through rows, we'll start looping through columns, and we'll add the condition of printing the row number in the first column for each row.

for(var j=0;j<colValue;j++){
if (j==0) {
outputString+='<td width=21><center>'+(i+1)+'</center></td>';
}

As we loop, we need to check our Active Array status from the form to see if we will draw an "active" button, or a regular button.

var x = ((i+1)*2)-1;
var y = ((i+1)*2);
if ((i+1)==ioString[x]&&(j+1)==ioString[y]) {
var buttonState="red";
}
else {
var buttonState="zero";
}

Remember the Y1Y1Y2Y2Y3Y3 array? As we sweep through the rows and columns, we compare our current row and column position as i & j to the values represented in the ioString array as x & y and set the button state; however, we are only using the row position to figure out which column should be set. All x values are the odd numbers in the ioString array, and all y values are the even numbers in the array.


We are still in the column loop at this point. Now we write the actual content for each cell. You can put in just about anything you want. Here's mine:

outputString+='<td width=21><a href=\"javascript:void(0);\"><span 
onMouseOut=\"MM_swapImgRestore()\;\" onMouseOver=\"MM_swapImage 
(\'button_r'+i+'c'+j+'\',\'\',\'button_state_one.png\')\;\"><img 
src=\"button_state_'+buttonState+'.png\" name=\"button_r'+i+'c'+j+'\" 
onClick=\"parent.alert(\'Clicked Row '+(i+1)+' Column '+(j+1)+'\')\;\" width=21 
height=21 border=0 alt=\"'+'row:'+(i+1)+' column:'+(j+1)+'\"></span></a></td>'
} 

In my table cell, I've included the functions from my canned Macromedia JavaScript that will roll-over my button image. I've set the button name to include the values from the current row and column position. I've also set the onClick to an alert() function. Remember, this is all in an IFRAME, so if I want to call a function in my main window, I have to call it like parent.alert('alert text'); But like I said, the cell content can be anything, like this for example:

outputString+='<td>X</td> '

At the end of our column sweep, we close each row.

outputString+='</tr>\r\n'
}

Once we reach the end of our row sweep, we close the table and HTML.

outputString+='</table></body></HTML>'

Earlier we created a dynamic IFRAME, which had its height and width attributes set to only one pixel. Now that we know the dimensions of the table, and since the outputString is set, we can set the size of the IFRAME.

document.getElementById("dynaTable").width=(24*colValue)+100;
document.getElementById("dynaTable").height=(24*rowValue)+100;

Finally, we get to write the output of the Table we've built.

document.getElementById("dynaTable").contentWindow.document.write('');
document.getElementById("dynaTable").contentWindow.document.write(outputString);
document.getElementById("dynaTable").contentWindow.document.close();

We don't use innerHTML here, it doesn't work with the IFRAME tag. The first blanking line, and the third closing line will allow us to refresh the page or re-generate the table. Without them, you would end up adding table after table to the contentWindow.


Last, we close the function. I included a write to a textarea form element so that the user can see the raw HTML generated, which is handy for dev purposes.

document.getElementById("formTextarea").value=outputString;
}

All done... :)