treewalker: child-sibling question

  • Thread starter Ward Germonpré
  • Start date
W

Ward Germonpré

Hi,

On a html page I have a Table called "resultaattbl", generated using dom-
methods and an array of jsobjects. The rows below the columnheader have a
class called "normaal". This is the layout:

...
<tr class=normaal>
<td><td>
<td><a href><img outlook-icon /></a>
<td>this text I want to alert!</td>
<td>...
</tr>
<tr class=normaal>
...

In the code below I try to alert the third cell of every row of my table,
but I get 'null' as value. Replacing nodeValue with textContent yields
the complete rowcontent, so I must be pretty close!


mijnfilter=function(node) {
if (node.className=="gewoon" )
return NodeFilter.FILTER_ACCEPT
else
return NodeFilter.FILTER_SKIP
}

var rootnode=document.getElementById("resultaattbl")
var walker=document.createTreeWalker(rootnode,
NodeFilter.SHOW_ELEMENT,mijnfilter,false)
do {
walker.firstChild(); //(now it points at the first TD, right ?
walker.nextSibling(); //(now it points at the second TD, right ?
walker.nextSibling(); //(now it points at the third TD, right ?

alert(walker.currentNode.nodeValue); //so why is this 'null' ?
} while (walker.nextNode())

thx for any help

Ward
 
E

Elegie

Ward Germonpré wrote:

Hi,
In the code below I try to alert the third cell of every row of my table,
but I get 'null' as value. Replacing nodeValue with textContent yields
the complete rowcontent, so I must be pretty close!

If your textContent property returns the whole row instead of the
cell... then the node upon which it is looked up is probably the row and
not the cell. Try and insert assertions to check the nodeType/nodeName
properties in your code.

walker.nextSibling(); //(now it points at the third TD, right ?

alert(walker.currentNode.nodeValue); //so why is this 'null' ?

The nodeValue of a node of type ELEMENT is always null, what you're
looking for is the nodeValue of the text node (the first child of your
element).

<URL:http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-F68D080>


Kind regards,
Elegie.
 
J

Jim

Ward,
This is much more simplified, perhaps too much for what you have, but
it might work for you:
---------------------------
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>
</title>
<meta http-equiv="Content-Type" content="text/html;
charset=iso-8859-1">
<script type="text/javascript" >
function doAlert(){
var tr_tags = document.getElementsByTagName("tr");
for(x=0; x<tr_tags.length; x++){
if(tr_tags[x].className == "normaal"){
tr_tags[x].cells[2].style.backgroundColor="red";
}
}
}
</script>
<style type="text/css">
table .normaal {
background-color:yellow;
border:2px red dashed;
color:navy;
}
</style>
</head>
<body onLoad="doAlert()">
<table>
<tr class="normaal">
<td>cell one</td>
<td>cell two</td>
<td>cell three</td>
<td>cell four</td>
</tr>
<tr class="normaal">
<td>cell one</td>
<td>cell two</td>
<td>cell three</td>
<td>cell four</td>
</tr>
<tr class="normaal">
<td>cell one</td>
<td>cell two</td>
<td>cell three</td>
<td>cell four</td>
</tr>
</table>
</body>
</html>
---------------------------------------------------
 
W

Ward Germonpré

Ward,
This is much more simplified, perhaps too much for what you have, but
it might work for you:
---------------------------
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>
</title>
<meta http-equiv="Content-Type" content="text/html;
charset=iso-8859-1">
<script type="text/javascript" >
function doAlert(){
var tr_tags = document.getElementsByTagName("tr");
for(x=0; x<tr_tags.length; x++){
if(tr_tags[x].className == "normaal"){
tr_tags[x].cells[2].style.backgroundColor="red";
}
}
}
</script>
<style type="text/css">
table .normaal {
background-color:yellow;
border:2px red dashed;
color:navy;
}
</style>
</head>
<body onLoad="doAlert()">
<table>
<tr class="normaal">
<td>cell one</td>
<td>cell two</td>
<td>cell three</td>
<td>cell four</td>
</tr>
<tr class="normaal">
<td>cell one</td>
<td>cell two</td>
<td>cell three</td>
<td>cell four</td>
</tr>
<tr class="normaal">
<td>cell one</td>
<td>cell two</td>
<td>cell three</td>
<td>cell four</td>
</tr>
</table>
</body>
</html>


An elegant way of solving this problem Jim, but I wanted to learn the dom
way of traversing a tree.

My table has a 'row insert function', invoked by clicking a plus.
Now it just inserts blank cells but I intend to insert data looked up
from a haystack, using a needle value from the row above. I want to be
proficient at walking around a table using a treewalker instead of index
techniques.

Then again your solution has the added benefit of being IE compatible.
This is an intranet, but still.. if treewalker fails I'll take your
direction.

thx,

Ward
 
W

Ward Germonpré

Ward Germonpré wrote:

Hi,


If your textContent property returns the whole row instead of the
cell... then the node upon which it is looked up is probably the row
and not the cell. Try and insert assertions to check the
nodeType/nodeName properties in your code.

<URL:http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#
Node3-textContent>


The nodeValue of a node of type ELEMENT is always null, what you're
looking for is the nodeValue of the text node (the first child of your
element).

<URL:http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#
ID-F68D080>


Kind regards,
Elegie.


Thx Elegie,

If I understand you correctly you're advising me to use
NodeFilter.SHOW_TEXT instead of SHOW_ELEMENT ?

I think something is wrong with my filterfunction, if I alert the
currentnode right after invoking the walker, it gives me TABLE, where it
should TR. NodeName and className confuse me, classname is an element
property I've used, but nodeName can yield 'class' and then its nodeValue
yields 'gewoon'. But a node is an element, right, like a tag, so how can
a nodeName be 'class' ?

Missed something early on, I need to read up.

thanks for your help

Ward
 
E

Elegie

Ward Germonpré wrote:

Hi,

It seems to me that you try and experiment with the DOM Core and DOM
Traversal without really having understood the specifications: giving
them some reading would certainly help to dissipate the fog you're
struggling in currently :)

<URL:http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html>
If I understand you correctly you're advising me to use
NodeFilter.SHOW_TEXT instead of SHOW_ELEMENT ?

Not really.

This argument, "whatToShow", explains which node type(s) the tree should
consider in its logical view before handling them to your filter. There
are exactly 12 node types, which can be combined using the OR bit operator.

Apparently, you want to retrieve some text content from a particular
element. The DOM work in two directions, therefore you could implement
two different logics (at least).

[1] filter using SHOW_ELEMENT; when iterating, you have to make sure
that the element is of nodeName TD and that it is the third TD in its
row. You can then grab its text content directly using the textContent
property. Other ways of retrieving the text also are available, like
concatenating the nodeValue of the text nodes contained within the TD,
get the string value of a matched text range, and so forth.

[2] filter using SHOW_TEXT; when iterating, you have to make sure that
one of the ascendant of the text node is a TD and that this TD is the
third in its row. This way, you will have determined that the text
belongs to the TD; however you're not finished, because some other text
nodes might be included in the TD - some cache process should therefore
be implemented in order to calculate the correct text value in the end.

Of course, the first solution is the simplest, all the more than an
efficient filter makes it a one line statement (see below).

I think something is wrong with my filterfunction,

The filter function should theoretically be fine, although I question
whether it is appropriate. I can see at first that you accept or reject
some node based on their CSS Class attribute, but that you provide some
HTML with TD having no CSS class atribute at all, resulting in their
being rejected in the end.

However, this function should be made part of an object literal, under
the named property "acceptNode". Also, using NodeFilter.REJECT moves
away the rejected node, including all its children; make sure you really
want to use REJECT and not SKIP.

if I alert the
currentnode right after invoking the walker, it gives me TABLE, where it
should TR.

No, the first node of the treewalker is the root node you have specified
when calling the createTreeWalker factory.

NodeName and className confuse me, classname is an element
property I've used, but nodeName can yield 'class' and then its nodeValue
yields 'gewoon'.

nodeName gives you the name of the node (eg. "TABLE", "TD", "#document",
"#text"...) while className gives you the CSS class, like in

<div class="cssFoo">

nodeName will never yield "class", unless you work with a node of type
ATTRIBUTE; still, since they are not part of the tree (they are
registered on the ELEMENT node), they will never appear in the
treewalker unless you define one explicitly as the root node of the
treewalker, using the getAttributeNode method for instance.

But a node is an element, right, like a tag

Not really. An element is a node of type ELEMENT, however 11 other node
types exist, such as TEXT, DOCUMENT, ENTITY, PROCESSING_INSTRUCTION,
COMMENT etc.

Here is a short example demonstrating the use of a treewalker to alert
the third TD content. Eventually, be informed that using a treewalker
over basic DOM parsing suffers several flaws: not only is it supported
in a very few browsers, but also very few programmers are familiar with
DOM Traversal (which could lead to a maintenance issue).


---
<style type="text/css">
td {
border : 1px solid #c00;
padding : 5px;
width : 200px;
}
</style>

<table id="foo">
<tbody>
<tr>
<td>Desert on the Moon</td>
<td>010101 (binary system)</td>
<td>Kung-Fu World Champion</td>
</tr>
<tr>
<td>Truth and Lies</td>
<td>Wind Song</td>
<td>XYZ</td>
</tr>
</tbody>
</table>

<script type="text/javascript">
window.onload = function() {
if(document.createTreeWalker) {
var n;
var tw = document.createTreeWalker(
document.getElementById("foo"),
NodeFilter.SHOW_ELEMENT,
{
acceptNode : function (node) {
return (
node.nodeName.toLowerCase()=="td" &&
node==node.parentNode.cells[2]
) ? NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP ;
}
},
false
);

while((n=tw.nextNode()) != null)
alert(n.nodeName + " : " + n.textContent);
}
}
</script>
---
 
R

RobG

Ward said:
Hi,
[...]

In the code below I try to alert the third cell of every row of my table,
but I get 'null' as value. Replacing nodeValue with textContent yields
the complete rowcontent, so I must be pretty close!


mijnfilter=function(node) {
if (node.className=="gewoon" )
return NodeFilter.FILTER_ACCEPT
else
return NodeFilter.FILTER_SKIP
}

var rootnode=document.getElementById("resultaattbl")
var walker=document.createTreeWalker(rootnode,
NodeFilter.SHOW_ELEMENT,mijnfilter,false)
do {
walker.firstChild(); //(now it points at the first TD, right ?
walker.nextSibling(); //(now it points at the second TD, right ?
walker.nextSibling(); //(now it points at the third TD, right ?

alert(walker.currentNode.nodeValue); //so why is this 'null' ?
} while (walker.nextNode())

There is a tutorial here on NodeFilter:

<URL: http://www.javascriptkit.com/dhtmltutors/treewalker.shtml >

I think you would be much better to use the table's rows collection,
then just grab the third cell of every row, something like:

var table = document.getElementById('resultaattbl');
var rows = table.rows;
var cell, cells;
for (var i=0, len=rows.length; i<len; i++) {
cells = rows.getElementsByTagName('td');
alert(cells[2]);
}

Your use of NodeFilter and textContent indicate that you'll only be
using this with Mozilla/Firefox or Opera, so you could replace the guts
of the for loop with:

alert(rows.cells[2]);

But since at least one modern browser doesn't properly support the
cells collection, I've used getElementsByTagName(td).

If you want to search the cells to find the ones with classname
'gewoon', then iterate over the rows thusly (again the cells collection
might be used instead of getElementsByTagName if this is for Mozilla &
Opera):

for (var i=0, len=rows.length; i<len; i++) {
cells = rows.getElementsByTagName('td');
for (var j=0, len2=cells.length; j<len2; j++){
cell = cells[j];
if (/\bgewoon\b/.test(cell.className) {
alert(cell.textContent);
}
}

You seem to be heading toward a 'getElementsByClassName' function,
there have been some posted before, search the archives.
 
W

Ward Germonpré

Hi,

On a html page I have a Table called "resultaattbl", generated using
dom- methods and an array of jsobjects. The rows below the
columnheader have a class called "normaal". This is the layout:

..
<tr class=normaal>
<td><td>
<td><a href><img outlook-icon /></a>
<td>this text I want to alert!</td>
<td>...
</tr>
<tr class=normaal>
..

In the code below I try to alert the third cell of every row of my
table, but I get 'null' as value. Replacing nodeValue with textContent
yields the complete rowcontent, so I must be pretty close!


mijnfilter=function(node) {
if (node.className=="gewoon" )
return NodeFilter.FILTER_ACCEPT
else
return NodeFilter.FILTER_SKIP
}

var rootnode=document.getElementById("resultaattbl")
var walker=document.createTreeWalker(rootnode,
NodeFilter.SHOW_ELEMENT,mijnfilter,false)
do {
walker.firstChild(); //(now it points at the first TD, right ?
walker.nextSibling(); //(now it points at the second TD, right ?
walker.nextSibling(); //(now it points at the third TD, right ?

alert(walker.currentNode.nodeValue); //so why is this 'null'
?
} while (walker.nextNode())

thx for any help

Ward

Thanks to all for your help, work is on the table.

I omitted that I use CSS: class "gewoon" is used for a mouseover effect,
the TD's inherit TR's color. I still need to read up, but from Elegie's
statement

<snip>"I can see at first that you accept or reject
some node based on their CSS Class attribute, but that you provide some
HTML with TD having no CSS class atribute at all, resulting in their
being rejected in the end.</snip>

the omission appears relevant.

I have my work cut out, thanks again everyone


Ward
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top