PDA

View Full Version : show / hide row in table


homerUK
08-24-2006, 10:30 AM
hi guys...

I'm having a problem showing and hiding a table row. I create it using the DOM insertRow() method, which all works fine.. but when I hide it, then click again to show the row, it appears underneath the first column.

The basic idea is that I am creating a tree structure of top level "menus". The user can then drill down through the menus and as they do, I want to show and hide sub menus. I dont want to load all these at the start (which would be easier) as there are so many. If the user requests say menuA, the code will go away and request all the sub menu data from the DB... (this bit is not the problem).

example: http://www.mattfacer.com/test/test.php (click on the links in the first column... they will effectivly toggle the data.

JavaScript Code:

/* array to store what's been loaded */
/* this stops the user calling the data from the database each time the row is shown/hidden */
var loaded = Array();

/* Load the arrays (this would come from the database) */
var sub_rows_1 = Array(
"<td>&nbsp;</td><td>sub1</td><td>sub2</td><td>sub3</td>",
"<td>&nbsp;</td><td>sub5</td><td>sub6</td><td>sub7</td>"
);

var sub_rows_2 = Array(
"<td>&nbsp;</td><td>sub ddd</td><td>sub eee</td><td>sub fff</td>",
"<td>&nbsp;</td><td>suby</td><td>subt</td><td>subu</td>"
);

/* function to show the row (this would fetch from the DB) */
/* ID is the row identifier */
function show_row(id) {

var tbl = document.getElementById("my_table");
var rowIndex = (document.getElementById("row_" + id).rowIndex + 1); //the row which we'll work under
var row_name = eval("sub_rows_" + id);

for (x=0;x<row_name.length;x++) {

var newRow = tbl.insertRow(rowIndex);
newRow.innerHTML = row_name[x];
newRow.className = "class_sub_" + id;
}

loaded.push(id);

}

/* toggle the row (show or hide depending on the row's status) */
function toggle(id) {
if (loaded.indexOf(id) < 0) {
//not added this tree yet, so do it
show_row(id);
} else {
//each sub row is labelled with a classname so it can be reffered to (id is meant to be unique!)
rows = getElementsByClass("class_sub_" + id, document, "tr");
for (x=0;x<rows.length;x++) {
if (rows[x].style.display == "none") {
rows[x].style.display = "block";
} else {
rows[x].style.display = "none";
}
}
}
}

/* find all elements by className */
function getElementsByClass(searchClass,node,tag) {
var classElements = new Array();
if ( node == null )
node = document;
if ( tag == null )
tag = '*';
var els = node.getElementsByTagName(tag);
var elsLen = els.length;
var pattern = new RegExp('(^|\\s)'+searchClass+'(\\s|$)');
for (i = 0, j = 0; i < elsLen; i++) {
if ( pattern.test(els[i].className) ) {
classElements[j] = els[i];
j++;
}
}
return classElements;
}


HTML code

<table id="my_table" border="1">
<tbody>
<tr>
<td>&nbsp;</td>
<td><strong>Col 1</strong></td>
<td><strong>Col 2</strong></td>
<td><strong>Col 3</strong></td>
</tr>

<tr id="row_1">
<td><a href="#" onClick="toggle(1); return false;">show</a></td>
<td>Data red</td>
<td>Data yekk</td>
<td>Data gren</td>
</tr>

<tr id="row_2">
<td><a href="#" onClick="toggle(2); return false;">clicky</a></td>
<td>Data x</td>
<td>Data y</td>
<td>Data z</td>
</tr>
</tbody>
</table>


any ideas if there is an easier way to hide / show table rows?!! (It HAS to work across all "popular" browsers... even IE :rolleyes: )

BTW: I am aware my example doesnt work in IE! So if anyone knows of a way, that'd be ace. Works ok (ish) in FF....

thanks for any help :)

Kor
08-24-2006, 12:20 PM
I doubt it works properly in FF, as, for table and table's elements display FF uses DOM values, not "block". You should have used
object.display="";
instead of
object.display="block";
if you want a crossbrowser approach

If you want to grab data from a DB, that will be a problem, as you need to do that asinchronuously, probably with AJAX. In this case, the problem will be not to hide/show rows, but to create/remove rows.

homerUK
08-24-2006, 12:54 PM
its not actually a menu I'm creating, its a table of data that has sub rows... I just used the menu as an example. I use DOM to create a new row, then paste innerHTML such as <td>blah</td><td>hello</td> etc

I've already got the Ajax code sorted - that's returning ok... I've just not included in this example.

All I needed to know at the mo is how to create the row, put in cell data then be able to hide the newly created rows.

Kor
09-01-2006, 12:34 PM
Could be something based on this example:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Untitled Document</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<script type="text/javascript">
var data=[
['Data red','Data yekk','Data gren'],
['Data x','Data y','Data z']
]
function addRows(){
var root=document.getElementById('my_table').getElementsByTagName('tbody')[0];
for(var i=0;i<data.length;i++){
var crow=document.createElement('tr');
crow.style.backgroundColor='#E5E5E5'
var ccel=document.createElement('td');
ccel.appendChild(document.createTextNode('show'));
ccel.setAttribute('colSpan',data[0].length);
ccel.style.cursor='pointer';
ccel.onclick=function(){showhide(this)}
crow.appendChild(ccel);
root.appendChild(crow);
var row=document.createElement('tr');
row.style.display='none';
for(var j=0;j<data[0].length;j++){
var cel=document.createElement('td');
cel.appendChild(document.createTextNode(data[i][j]))
row.appendChild(cel)
}
root.appendChild(row)
}
}
function showhide(c){
var next=c.parentNode.nextSibling;
while(next.nodeName.toLowerCase()!='tr'){
next=next.nextSibling;
}
next.style.display=(next.style.display=='')?'none':'';
c.firstChild.data=(c.firstChild.data=='show')?'hide':'show';
}
onload=addRows;
</script>
</head>
<body>
<table width="400" border="1" id="my_table">
<tbody>
<tr>
<td width="33%"><strong>Col 1</strong></td>
<td width="33%"><strong>Col 2</strong></td>
<td><strong>Col 3</strong></td>
</tr>
</tbody>
</table>
</body>
</html>

homerUK
09-04-2006, 09:49 AM
hey Kor,

thanks for the example - works nicely!

I'm just trying to modify the code you sent - as the rows which you create saying "show" I actually need to have as proper data - they will always be shown - it's the rows underneath that will be hidden/shown.

fingers crossed!! :)

Kor
09-04-2006, 10:43 AM
hey Kor,

thanks for the example - works nicely!

I'm just trying to modify the code you sent - as the rows which you create saying "show" I actually need to have as proper data - they will always be shown - it's the rows underneath that will be hidden/shown.

fingers crossed!! :)
eeerr, I don't get it... the rows with "show" are always shown, and "it's the rows underneath that will be hidden/shown", as you say... Which is the problem?

homerUK
09-04-2006, 11:28 AM
hi - sorry - not very clear I know!!

I want to create the table with the "show" rows using PHP, so not using Javascript. eg: the PHP would output

<tr>
<td>show</td>
</tr>

<tr>
<td>show</td>
</tr>

at the moment, the JS creates these rows... is there a way like im my original code to add sub rows to the selected row. Eg: the user clicks on the second row, then the second element in the array of data would appear below...?

sorry to be a pain!

Kor
09-04-2006, 12:46 PM
Like this?:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Untitled Document</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<script type="text/javascript">
var data=[
['Data red','Data yekk','Data gren'],
['Data x','Data y','Data z']
]
function showhide(c,i){
var rows=document.getElementById('my_table').getElementsByTagName('tr');
var next=c.parentNode.nextSibling;
var row;
if(c.parentNode.rowIndex==rows.length-1){
row= addRow(i);
rows[0].parentNode.appendChild(row)
c.firstChild.nodeValue='hide';
}
else{
while(next.nodeName.toLowerCase()!='tr'){next=next.nextSibling}
if(next.getAttributeNode('foo')){
next.style.display=next.style.display==''?'none':'';
c.firstChild.nodeValue=c.firstChild.nodeValue=='hide'?'show':'hide';
}
else{
row= addRow(i);
rows[0].parentNode.insertBefore(row,next);
c.firstChild.nodeValue='hide';
}
}
}
function addRow(i){
var row=document.createElement('tr');
row.setAttribute('foo','fee');
for(var j=0;j<data[i].length;j++){
var cel=document.createElement('td');
cel.appendChild(document.createTextNode(data[i][j]))
row.appendChild(cel)
}
return row;
}
</script>
</head>
<body>
<table width="400" border="1" id="my_table">
<tbody>
<tr>
<td width="33%"><strong>Col 1</strong></td>
<td width="33%"><strong>Col 2</strong></td>
<td><strong>Col 3</strong></td>
</tr>
<tr>
<td colspan="3" onclick="showhide(this,0)">show</td>
</tr>
<tr>
<td colspan="3" onclick="showhide(this,1)">show</td>
</tr>
</tbody>
</table>

</body>
</html>

homerUK
09-13-2006, 11:23 AM
hi again Kor!
Been playing with the code - it's nearly there but I am still confused as to how it all works...! I have created multiple arrays... eg:


var data0= [
['Data red','Data yekk','Data gren'],
['Data x','Data y','Data z']
]

var data1 = [
["test","test2","test3"],
["test5","test6","test7"],
["trterte","ergdbdfg","dfbdfbdrer"]
]


in your example, you have ONE array, "data" which has rows. I need to have multiple rows per array.... as each "drop down" will have multiple rows. Eg: you click the first row, it would add the two rows (from data0 array)... if you then clicked the second row, it would add data1 array (add three rows)...


function addRow(i){
var row=document.createElement('tr');
row.setAttribute('foo','fee');
for(var j=0;j<data+i.length;j++){
var cel=document.createElement('td');
cel.appendChild(document.createTextNode(data+i[j]))
row.appendChild(cel)
}
return row;
}


I tried changing the data+i as I thought I could use the i being returned from the showHide function attached to the rows. its not working though!!

sorry to be a pain!!

Kor
09-13-2006, 11:26 AM
.........

Kor
09-13-2006, 11:28 AM
data1 and data2 are global variables, thus they can be taken as window's properties

for(var j=0;j<window['data'+i].length;j++){

homerUK
09-13-2006, 11:59 AM
cool - nearly there now!!!


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Untitled Document</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<script type="text/javascript">
var data0= [
['Data red','Data yekk','Data gren'],
['Data x','Data y','Data z'],
['Data 55','Data 56','Data 67'],
['Data fgdfg','Data dfgd f','Data sdfwser']
]

var data1 = [
['Data red','Data yekk','Data gren'],
['Data x','Data y','Data z']
]

function showhide(c,i){
var rows=document.getElementById('my_table').getElementsByTagName('tr');
var next=c.parentNode.nextSibling;
var row;
if(c.parentNode.rowIndex==rows.length-1){
row = addRow(i);
rows[0].parentNode.appendChild(row)
c.firstChild.nodeValue='hide';
}
else{
while(next.nodeName.toLowerCase()!='tr'){next=next.nextSibling}
if(next.getAttributeNode('foo')){
next.style.display=next.style.display==''?'none':'';
c.firstChild.nodeValue=c.firstChild.nodeValue=='hide'?'show':'hide';
}
else{

//add rows
for(var j=0;j<window["data" +i].length;j++){
row = addRow(i,j);
rows[0].parentNode.insertBefore(row,next);
}
c.firstChild.nodeValue='hide';
}
}
}
function addRow(i,j){
var row = document.createElement('tr');
row.setAttribute('foo','fee');

//now go through each sub elements
for (var k=0; k<window["data" +i][j].length; k++) {
var cel=document.createElement('td');
cel.appendChild(document.createTextNode(window["data" +i][j][k]))
row.appendChild(cel)
}
return row;
}
</script>
</head>
<body>
<table width="400" border="1" id="my_table">
<tbody>
<tr>
<td width="33%"><strong>Col 1</strong></td>
<td width="33%"><strong>Col 2</strong></td>
<td><strong>Col 3</strong></td>
</tr>
<tr>
<td colspan="3" onclick="showhide(this,0)">show</td>
</tr>
<tr>
<td colspan="3" onclick="showhide(this,1)">show</td>
</tr>
</tbody>
</table>

</body>
</html>


that will show the first 4 rows - but only hides the first one of them... and the bottom row will now show any!!

any ideas?! I imagine it's something to do with the looping FOR ?

(PS - thanks v much for this help - awesome!!)