Iterate through table rows

A

annoyingmouse

Hi there,

Okay, I'm guessing I'll slap myself in the forehead once I grok it
properly but I'm trying to hide and show table rows without using an
external library.

Each of the rows has a class attribute. There is one of "typeOneRow"
followed by 0 to an arbitrary number of "typeTwoRow" before the next
"typeOneRow" and so one for an arbitrary number of rows. Like:

<tr class="typeOneRow"><td>Some Text <span onclick=""></span></
td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeOneRow"><td>More Text <span onclick=""></span></
td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeOneRow"><td>Something <span onclick=""></span></
td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeOneRow"><td>Wibble <span onclick=""></span></td><td></
td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>

Within the "typeOneRow" there are two cells, within the first there is
some text and a span which, when clicked, calls a function which gets
the text content of the cell (var searchedForContent =
span.parentNode.firstChild.nodeValue;). The function then iterates
through the rows of the table until it finds the row with the cell
which has content the same as searchedForContent.

Now what I want to do next is alter the style attribute of each
subsequent row (the "typeTwoRow" rows) until the next "typeOneRow" is
found. I figure it's possible it's just that I'm getting lost within
loops.

As an aside, is there a better way of getting the class name of an
element other than using .getAttribute("class")?

Cheers,

Dom
 
A

annoyingmouse

Hi there,

Okay, I'm guessing I'll slap myself in the forehead once I grok it
properly but I'm trying to hide and show table rows without using an
external library.

Each of the rows has a class attribute. There is one of "typeOneRow"
followed by 0 to an arbitrary number of "typeTwoRow" before the next
"typeOneRow" and so one for an arbitrary number of rows. Like:

<tr class="typeOneRow"><td>Some Text <span onclick=""></span></
td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeOneRow"><td>More Text <span onclick=""></span></
td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeOneRow"><td>Something <span onclick=""></span></
td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeOneRow"><td>Wibble <span onclick=""></span></td><td></
td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>

Within the "typeOneRow" there are two cells, within the first there is
some text and a span which, when clicked, calls a function which gets
the text content of the cell (var searchedForContent =
span.parentNode.firstChild.nodeValue;). The function then iterates
through the rows of the table until it finds the row with the cell
which has content the same as searchedForContent.

Now what I want to do next is alter the style attribute of each
subsequent row (the "typeTwoRow" rows) until the next "typeOneRow" is
found. I figure it's possible it's just that I'm getting lost within
loops.

As an aside, is there a better way of getting the class name of an
element other than using .getAttribute("class")?

Cheers,

Dom

Solved it!

What I had was:

function showCategories(span){
var searchedForContent = span.parentNode.firstChild.nodeValue;
var theTable = document.getElementById("myTable");
var tableRows = theTable.getElementsByTagName("tr");
var foundIt = false;
for(var aRow in tableRows){
if(tableRows[aRow].firstChild.firstChild.nodeValue ==
searchedForContent){
foundIt = true;
}
if(foundIt && tableRows[aRow].getAttribute("class") !=
"typeOneRow"){
tableRows[aRow].removeAttribute("style");
tableRows[aRow].setAttribute("style", "display: table-row;");
}
}
}

Which worked a treat except it kept on showing all following rows and
didn't stop at the next "typeOneRow" so I started thinking about
adding another flag and came up with this:

function showCategories(span){
var searchedForContent = span.parentNode.firstChild.nodeValue;
var theTable = document.getElementById("myTable");
var tableRows = theTable.getElementsByTagName("tr");
var foundIt = false;
var finishedShowing = false;
for(var aRow in tableRows){
if(finishedShowing){
break;
}
if(foundIt && tableRows[aRow].getAttribute("class") ==
"typeOneRow"){
finishedShowing = true;
}
if(tableRows[aRow].firstChild.firstChild.nodeValue ==
searchedForContent){
foundIt = true;
}
if(foundIt && tableRows[aRow].getAttribute("class") !=
"typeOneRow"){
tableRows[aRow].removeAttribute("style");
tableRows[aRow].setAttribute("style", "display: table-row;");
}
}
}

Which does what I want but throws an exception when I click the span
on the last "visible" row...?

tableRows[aRow].getAttribute is not a function

Any ideas?

Cheers,

Dom
 
R

Richard Cornford

Hi there,

Okay, I'm guessing I'll slap myself in the forehead once I grok
it properly but I'm trying to hide and show table rows without
using an external library.

So a trivial thing to do.
Each of the rows has a class attribute. There is one of
"typeOneRow" followed by 0 to an arbitrary number of
"typeTwoRow" before the next "typeOneRow" and so one for
an arbitrary number of rows. Like:

<tr class="typeOneRow"><td>Some Text <span onclick=""></span></td>
<td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeTwoRow" style="display:none;"><td></td><td></td></tr>
<tr class="typeOneRow"><td>More Text <span onclick=""></span></
td><td></td></tr>
Within the "typeOneRow" there are two cells, within the first
there is some text and a span which, when clicked, calls a
function which gets the text content of the cell (var
searchedForContent = span.parentNode.firstChild.nodeValue;).

Given the mark-up, that should, or could, work, so long as - span -
refers to the correct element.
The function then iterates
through the rows of the table

Using the TABLE element's - rows - collection?
until it finds the row with the cell
which has content the same as searchedForContent.

That is an over complex search strategy. It would make more sense to
get a reference to the TR element that contained the SPAN
(span.parentNode.parentNode -- SPAN -> TD -> TR) and compare the
identity of that element with each TR found while iterating over the
rows. Then again, if TR elements have (working) - rowIndex -
properties then you can directly know the starting point in the
TABLE's - rows - collection.
Now what I want to do next is alter the style attribute of
each subsequent row (the "typeTwoRow" rows)

Or more likely you want to modify the properties of the TR element's -
style - object (change its - display - property), as messing around
with attributes is much more complex.
until the next "typeOneRow" is found. I figure it's possible

Entirely possible.
it's just that I'm getting lost within loops.

What loops (i.e. where is the code you are using)?
As an aside, is there a better way of getting the class name
of an element other than using .getAttribute("class")?

You would hope so as - getAttribute - is easily the worst way (try it
in IE 6). DOM elements have a - className - property, which is the
best means of accessing and setting the classes that apply to an
element (and the last browsers that did not support that property have
long since dropped out of use (e.g. Netscape 4).

Richard.
 
R

Richard Cornford

<
Solved it!

Then why the question about an exception being thrown? I would think
that, at minimum, "solved" would be when the code did what you wanted
it to do (reliably, and in the full set of browsers that is
appropriate for its context) without any errors at all.
What I had was:

function showCategories(span){

You are not showing how this is called, and so not showing how - span
- is being assigned its value.
var searchedForContent = span.parentNode.firstChild.nodeValue;
var theTable = document.getElementById("myTable");

Given that in an HTML DOM a TR must be directly contained in one of
TBODY, THEAD or TFOOT, and each of those must be directly contained in
a TABLE then:-

span.parentNode.parentNode.parentNode.parentNode

- should be a safe, reliable and fast (in comparison to
getElementById) means of getting to the TABLE element from the SPAN.
var tableRows = theTable.getElementsByTagName("tr");

var foundIt = false;
for(var aRow in tableRows){

Even if - theTable.getElementsByTagName("tr") - returns a NodeList of
all of the elements in DOM order (which it should), the for-in loop is
not required to iterate the properties of any object in any particular
order (at least by the ES3 spec for javascript). Which is a problem as
the order of iteration is critical here.

There is an additional problem in that objects implanting the DOM
NodeList interface will have - item - and - length - properties that
do not refer to DOM elements, and may have many others as implementing
an interface means providing a minimum set of properties and method,
but makes no impositions on the object implanting the interface beyond
that.

It would make more sense to iterate through the NodeList using its -
length - property to constrain an ordinary - for - loop with a
counter. I.E.:-

for(var c = 0;c < tableRows.length;++c){
if(tableRows[c]. ... ){
...
}
...
}

- and so only be acting on TR elements.
if(tableRows[aRow].firstChild.firstChild.nodeValue ==
searchedForContent){
foundIt = true;
}
if(foundIt && tableRows[aRow].getAttribute("class") !=
"typeOneRow"){
tableRows[aRow].removeAttribute("style");
tableRows[aRow].setAttribute("style", "display: table-row;");

These pervious two lines could be replaced with:-

tableRows[aRow].style.display = 'table-row';

However, support for the table-row CSS property is recent and not
100%. As the rows are hidden with display:none; in their STYLE
attributes they could be shown with:-

tableRows[aRow].style.display = '';

- where assigning the empty string has the effect of removing the
'none' and having the element revert to its default (or inherited/
cascaded) - display - value, be that 'table-row', 'block', or
whatever. Thus this method of revealing the element's is not dependent
on the browsers support for recent CSS features.
}
}

}

Which worked a treat except it kept on showing all following
rows and didn't stop at the next "typeOneRow" so I started
thinking about adding another flag and came up with this:

function showCategories(span){
var searchedForContent = span.parentNode.firstChild.nodeValue;
var theTable = document.getElementById("myTable");
var tableRows = theTable.getElementsByTagName("tr");
var foundIt = false;
var finishedShowing = false;
for(var aRow in tableRows){
if(finishedShowing){
break;
}
if(foundIt && tableRows[aRow].getAttribute("class") ==
"typeOneRow"){
finishedShowing = true;
}
if(tableRows[aRow].firstChild.firstChild.nodeValue ==
searchedForContent){
foundIt = true;
}
if(foundIt && tableRows[aRow].getAttribute("class") !=
"typeOneRow"){
tableRows[aRow].removeAttribute("style");
tableRows[aRow].setAttribute("style", "display: table-row;");
}
}

}

Which does what I want but throws an exception when I click
the span on the last "visible" row...?

tableRows[aRow].getAttribute is not a function

The - length - and - item - properties of - tableRows - are a number
and a host method respectively. Neither of those will have a -
getAttribute - method, so their - getAttribute - properties will not
be functions. Hence the error.

Richard.
 
A

annoyingmouse

<
Solved it!

Then why the question about an exception being thrown? I would think
that, at minimum, "solved" would be when the code did what you wanted
it to do (reliably, and in the full set of browsers that is
appropriate for its context) without any errors at all.
What I had was:
function showCategories(span){

You are not showing how this is called, and so not showing how - span
- is being assigned its value.
  var searchedForContent = span.parentNode.firstChild.nodeValue;
  var theTable = document.getElementById("myTable");

Given that in an HTML DOM a TR must be directly contained in one of
TBODY, THEAD or TFOOT, and each of those must be directly contained in
a TABLE then:-

span.parentNode.parentNode.parentNode.parentNode

- should be a safe, reliable and fast  (in comparison to
getElementById) means of getting to the TABLE element from the SPAN.
  var tableRows = theTable.getElementsByTagName("tr");
  var foundIt = false;
  for(var aRow in tableRows){

Even if - theTable.getElementsByTagName("tr") - returns a NodeList of
all of the elements in DOM order (which it should), the for-in loop is
not required to iterate the properties of any object in any particular
order (at least by the ES3 spec for javascript). Which is a problem as
the order of iteration is critical here.

There is an additional problem in that objects implanting the DOM
NodeList interface will have - item - and - length - properties that
do not refer to DOM elements, and may have many others as implementing
an interface means providing a minimum set of properties and method,
but makes no impositions on the object implanting the interface beyond
that.

It would make more sense to iterate through the NodeList using its -
length - property to constrain an ordinary - for - loop with a
counter. I.E.:-

for(var c = 0;c < tableRows.length;++c){
    if(tableRows[c]. ... ){
        ...
    }
    ...

}

- and so only be acting on TR elements.
    if(tableRows[aRow].firstChild.firstChild.nodeValue ==
searchedForContent){
      foundIt = true;
    }
    if(foundIt && tableRows[aRow].getAttribute("class") !=
"typeOneRow"){
      tableRows[aRow].removeAttribute("style");
      tableRows[aRow].setAttribute("style", "display: table-row;");

These pervious two lines could be replaced with:-

tableRows[aRow].style.display = 'table-row';

However, support for the table-row CSS property is recent and not
100%. As the rows are hidden with display:none; in their STYLE
attributes they could be shown with:-

tableRows[aRow].style.display = '';

- where assigning the empty string has the effect of removing the
'none' and having the element revert to its default (or inherited/
cascaded) - display - value, be that 'table-row', 'block', or
whatever. Thus this method of revealing the element's is not dependent
on the browsers support for recent CSS features.


    }
  }

Which worked a treat except it kept on showing all following
rows and didn't stop at the next "typeOneRow" so I started
thinking about adding another flag and came up with this:
function showCategories(span){
  var searchedForContent = span.parentNode.firstChild.nodeValue;
  var theTable = document.getElementById("myTable");
  var tableRows = theTable.getElementsByTagName("tr");
  var foundIt = false;
  var finishedShowing = false;
  for(var aRow in tableRows){
    if(finishedShowing){
      break;
    }
    if(foundIt && tableRows[aRow].getAttribute("class") ==
"typeOneRow"){
      finishedShowing = true;
    }
    if(tableRows[aRow].firstChild.firstChild.nodeValue ==
searchedForContent){
      foundIt = true;
    }
    if(foundIt && tableRows[aRow].getAttribute("class") !=
"typeOneRow"){
      tableRows[aRow].removeAttribute("style");
      tableRows[aRow].setAttribute("style", "display: table-row;");
    }
  }

Which does what I want but throws an exception when I click
the span on the last "visible" row...?
tableRows[aRow].getAttribute is not a function

The - length - and - item - properties of - tableRows - are a number
and a host method respectively. Neither of those will have a -
getAttribute - method, so their - getAttribute - properties will not
be functions. Hence the error.

Richard.

Hi Richard,

thank you ever so for your time!

I've looked over what you've said and came up with this:

function showCategories(span){
var searchedForContent = span.parentNode.firstChild.nodeValue;
var theTable = span.parentNode.parentNode.parentNode.parentNode;
// Presentational stuff this:
var mainCell1SpanAlternative = document.createElement("span");
mainCell1SpanAlternative.className = "removeCategories";

mainCell1SpanAlternative.setAttribute("onclick","hideCategories(this)");
var mainCell1SpanAlternativeContent =
document.createTextNode("[-]");

mainCell1SpanAlternative.appendChild(mainCell1SpanAlternativeContent);
span.parentNode.replaceChild(mainCell1SpanAlternative, span);
var tableRows = theTable.getElementsByTagName("tr");
var foundIt = false;
var finishedShowing = false;
for(var aRow = 0; aRow < tableRows.length; ++aRow){
if(finishedShowing){
break;
}
if(foundIt && tableRows[aRow].className == "typeOneRow"){
finishedShowing = true;
}
if(tableRows[aRow].firstChild.firstChild.nodeValue ==
searchedForContent){
foundIt = true;
}
if(foundIt && tableRows[aRow].className != "typeOneRow"){
tableRows[aRow].style.display = '';
}
}
}

The table itself is generated by script but the basic "computed"
layout is:

<table id="myTable">
<colgroup><col width="80%"><col width="20%"></colgroup>
<thead>
<tr><th>Something</th><th>Else</th></tr>
</thead>
<tbody>
<tr class="typeOneRow"><td class="area">Somewhere <span
onclick="showCategories(this)" class="addCategories">[+]</span></
td><td class="grossExpenditure">100</td></tr>
<tr class="typeTwoRow" style="display: none;"><td
class="detail">Thing01</td><td class="expenditure">50</td></tr>
<tr class="typeTwoRow" style="display: none;"><td
class="detail">Thing02</td><td class="expenditure">40</td></tr>
<tr class="typeTwoRow" style="display: none;"><td
class="detail">Thing03</td><td class="expenditure">10</td></tr>
.... and so one ...
</tbody>
</table>

As you can perhaps tell there is a corresponding function which hides
the rows upon the added "[-]" being clicked, which is basically the
one above but with "tableRows[aRow].style.display = 'none';" added and
the "[-]" replaced with a "[+]".

Thanks again,

Dom
 
A

annoyingmouse

On Jun 23, 10:54 am, (e-mail address removed) wrote:
Then why the question about an exception being thrown? I would think
that, at minimum, "solved" would be when the code did what you wanted
it to do (reliably, and in the full set of browsers that is
appropriate for its context) without any errors at all.
You are not showing how this is called, and so not showing how - span
- is being assigned its value.
Given that in an HTML DOM a TR must be directly contained in one of
TBODY, THEAD or TFOOT, and each of those must be directly contained in
a TABLE then:-

- should be a safe, reliable and fast  (in comparison to
getElementById) means of getting to the TABLE element from the SPAN.
Even if - theTable.getElementsByTagName("tr") - returns a NodeList of
all of the elements in DOM order (which it should), the for-in loop is
not required to iterate the properties of any object in any particular
order (at least by the ES3 spec for javascript). Which is a problem as
the order of iteration is critical here.
There is an additional problem in that objects implanting the DOM
NodeList interface will have - item - and - length - properties that
do not refer to DOM elements, and may have many others as implementing
an interface means providing a minimum set of properties and method,
but makes no impositions on the object implanting the interface beyond
that.
It would make more sense to iterate through the NodeList using its -
length - property to constrain an ordinary - for - loop with a
counter. I.E.:-
for(var c = 0;c < tableRows.length;++c){
    if(tableRows[c]. ... ){
        ...
    }
    ...

- and so only be acting on TR elements.
    if(tableRows[aRow].firstChild.firstChild.nodeValue ==
searchedForContent){
      foundIt = true;
    }
    if(foundIt && tableRows[aRow].getAttribute("class") !=
"typeOneRow"){
      tableRows[aRow].removeAttribute("style");
      tableRows[aRow].setAttribute("style", "display: table-row;");
These pervious two lines could be replaced with:-
tableRows[aRow].style.display = 'table-row';
However, support for the table-row CSS property is recent and not
100%. As the rows are hidden with display:none; in their STYLE
attributes they could be shown with:-
tableRows[aRow].style.display = '';
- where assigning the empty string has the effect of removing the
'none' and having the element revert to its default (or inherited/
cascaded) - display - value, be that 'table-row', 'block', or
whatever. Thus this method of revealing the element's is not dependent
on the browsers support for recent CSS features.
    }
  }
}
Which worked a treat except it kept on showing all following
rows and didn't stop at the next "typeOneRow" so I started
thinking about adding another flag and came up with this:
function showCategories(span){
  var searchedForContent = span.parentNode.firstChild.nodeValue;
  var theTable = document.getElementById("myTable");
  var tableRows = theTable.getElementsByTagName("tr");
  var foundIt = false;
  var finishedShowing = false;
  for(var aRow in tableRows){
    if(finishedShowing){
      break;
    }
    if(foundIt && tableRows[aRow].getAttribute("class") ==
"typeOneRow"){
      finishedShowing = true;
    }
    if(tableRows[aRow].firstChild.firstChild.nodeValue ==
searchedForContent){
      foundIt = true;
    }
    if(foundIt && tableRows[aRow].getAttribute("class") !=
"typeOneRow"){
      tableRows[aRow].removeAttribute("style");
      tableRows[aRow].setAttribute("style", "display: table-row;");
    }
  }
}
Which does what I want but throws an exception when I click
the span on the last "visible" row...?
tableRows[aRow].getAttribute is not a function
The - length - and - item - properties of - tableRows - are a number
and a host method respectively. Neither of those will have a -
getAttribute - method, so their - getAttribute - properties will not
be functions. Hence the error.

Hi Richard,

thank you ever so for your time!

I've looked over what you've said and came up with this:

function showCategories(span){
  var searchedForContent = span.parentNode.firstChild.nodeValue;
  var theTable = span.parentNode.parentNode.parentNode.parentNode;
  // Presentational stuff this:
    var mainCell1SpanAlternative = document.createElement("span");
    mainCell1SpanAlternative.className = "removeCategories";

mainCell1SpanAlternative.setAttribute("onclick","hideCategories(this)");

Changing this to mainCell1SpanAlternative.onclick = function()
{ hideCategories(this))};

Means that it now works in IE6 as well.

Thanks again Richard.
    var mainCell1SpanAlternativeContent =
document.createTextNode("[-]");

mainCell1SpanAlternative.appendChild(mainCell1SpanAlternativeContent);
    span.parentNode.replaceChild(mainCell1SpanAlternative, span);
  var tableRows = theTable.getElementsByTagName("tr");
  var foundIt = false;
  var finishedShowing = false;
  for(var aRow = 0; aRow < tableRows.length; ++aRow){
    if(finishedShowing){
      break;
    }
    if(foundIt && tableRows[aRow].className == "typeOneRow"){
      finishedShowing = true;
    }
    if(tableRows[aRow].firstChild.firstChild.nodeValue ==
searchedForContent){
      foundIt = true;
    }
    if(foundIt && tableRows[aRow].className != "typeOneRow"){
      tableRows[aRow].style.display = '';
    }
  }

}

The table itself is generated by script but the basic "computed"
layout is:

<table id="myTable">
<colgroup><col width="80%"><col width="20%"></colgroup>
<thead>
<tr><th>Something</th><th>Else</th></tr>
</thead>
<tbody>
<tr class="typeOneRow"><td class="area">Somewhere <span
onclick="showCategories(this)" class="addCategories">[+]</span></
td><td class="grossExpenditure">100</td></tr>
<tr class="typeTwoRow" style="display: none;"><td
class="detail">Thing01</td><td class="expenditure">50</td></tr>
<tr class="typeTwoRow" style="display: none;"><td
class="detail">Thing02</td><td class="expenditure">40</td></tr>
<tr class="typeTwoRow" style="display: none;"><td
class="detail">Thing03</td><td class="expenditure">10</td></tr>
... and so one ...
</tbody>
</table>

As you can perhaps tell there is a corresponding function which hides
the rows upon the added "[-]" being clicked, which is basically the
one above but with "tableRows[aRow].style.display = 'none';" added and
the "[-]" replaced with a "[+]".

Thanks again,

Dom
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top