473,769 Members | 2,232 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Walk DOM Tree in Reverse?

It's quite simple to walk to the DOM tree going forward however I can't
figure out a nice clean way to walk the DOM tree in reverse. Checking
previousSibling is not sufficient as the previousSibling could be a
node which has childNodes and therefore the 'true' previousSibling
would be the *deepest* lastChild of the previousSibling ...

For example, given this graph:

1 myAnchor nodeType 1
2 myAnchorText1 nodeType 3
3 myAnchorText2 nodeType 3
4 myBold nodeType 1
5 myText1 nodeType 3
6 myText2 nodeType 3

I want to be able to walk the tree in reverse starting from position 6
so that I get:

myText2
myText1
myBold
myAnchorText2
myAnchorText1
myAnchor

Any ideas?

Thanks,
Cliff.

Apr 2 '06 #1
22 5402
VK

de*******@gmail .com wrote:
It's quite simple to walk to the DOM tree going forward however I can't
figure out a nice clean way to walk the DOM tree in reverse. Checking
previousSibling is not sufficient as the previousSibling could be a
node which has childNodes and therefore the 'true' previousSibling
would be the *deepest* lastChild of the previousSibling ...

For example, given this graph:

1 myAnchor nodeType 1
2 myAnchorText1 nodeType 3
3 myAnchorText2 nodeType 3
4 myBold nodeType 1
5 myText1 nodeType 3
6 myText2 nodeType 3

I want to be able to walk the tree in reverse starting from position 6
so that I get:

myText2
myText1
myBold
myAnchorText2
myAnchorText1
myAnchor

Any ideas?


I might be missing your problem:- an actual HTML code would be helpful.
The tree is...uhm... a tree. It means that can be a lot of children
(leaves) but only one parent (branch) for each child. Or - using
database terms - these are one-to-many relations, not many-to-many.

Apr 2 '06 #2
de*******@gmail .com wrote:
It's quite simple to walk to the DOM tree going forward however I can't
figure out a nice clean way to walk the DOM tree in reverse. Checking
previousSibling is not sufficient as the previousSibling could be a
node which has childNodes and therefore the 'true' previousSibling
would be the *deepest* lastChild of the previousSibling ...
You have to recurse into the subtree of the previousSibling until
there are no more child nodes, i.e. until there is no lastChild.
For example, given this graph:

1 myAnchor nodeType 1
2 myAnchorText1 nodeType 3
3 myAnchorText2 nodeType 3
4 myBold nodeType 1
5 myText1 nodeType 3
6 myText2 nodeType 3

I want to be able to walk the tree in reverse starting from position 6
so that I get:

myText2
myText1
myBold
myAnchorText2
myAnchorText1
myAnchor

Any ideas?


myText2.previou sSibling....par entNode.previou sSibling.lastCh ild...
..previousSibli ng....parentNod e...

See?
PointedEars
Apr 2 '06 #3
de*******@gmail .com writes:
It's quite simple to walk to the DOM tree going forward however I can't
figure out a nice clean way to walk the DOM tree in reverse.


You want a depth-first right-to-left traversal of the nodes.
That sounds like a job for recursion:

---
function traverseDFRTL(e lement, action) {
for(var child = element.lastChi ld; child; child = child.previousS ibling) {
traverseDFRTL(c hild, action);
}
action(element) ;
}
---

Calling
---
traverseDFRTL(s omeElement, function(node) {
alert([node.tagName, node.nodeType]);
});
---
will alert the nodes in the desired order.
If you don't want recursion, you can do it iteratively as well, but it
will never be as simple.

/L
--
Lasse Reichstein Nielsen - lr*@hotpop.com
DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleD OM.html>
'Faith without judgement merely degrades the spirit divine.'
Apr 2 '06 #4
Lasse Reichstein Nielsen wrote:
---
function traverseDFRTL(e lement, action) {
for(var child = element.lastChi ld; child; child =
child.previousS ibling) {
traverseDFRTL(c hild, action);
}
action(element) ;
}
---

Calling
---
traverseDFRTL(s omeElement, function(node) {
alert([node.tagName, node.nodeType]);
});
---
will alert the nodes in the desired order.


Very nice. There is one catch, though: after the very first node of the
document tree has been processed, action() is called with `null', and if
the reference to the above function is passed as callback, there will be
a script error in the end ("null has no properties"). So either the
callback is modified so that `node.tagName' and `node.nodeType' are only
evaluated if `node' refers to an object, or the second call of `action'
should not take place if `element' does not refer to an object.
Furthermore, text nodes have no `tagName' property.

It would also be nice if one could use the return value of traverseDFTRL() ,
and to allow for another identifier without modifying the function body.

Hence the following suggestion:

function isMethod(a)
{
var t;
return (a && ((t = typeof a) == "function" || t == "object"));
}

function traverseDFRTL(e lement, action)
{
for (var child = (element || document.docume ntElement).last Child;
child;
child = child.previousS ibling)
{
arguments.calle e(child, action);
}

if (isMethod(actio n))
{
return action(element) ;
}
}

var nodes = [];

alert(traverseD FRTL(
null,
function(node)
{
if (node)
{
nodes.push(node .nodeName
+ (typeof node.nodeValue == "string"
? (' "' + node.nodeValue + '"')
: ""));
}

return nodes;
}).join("\n"));
PointedEars
Apr 2 '06 #5
Thomas 'PointedEars' Lahn <Po*********@we b.de> writes:
Lasse Reichstein Nielsen wrote:
---
function traverseDFRTL(e lement, action) {
for(var child = element.lastChi ld; child; child =
child.previousS ibling) {
traverseDFRTL(c hild, action);
}
action(element) ;
}
---

Calling
---
traverseDFRTL(s omeElement, function(node) {
alert([node.tagName, node.nodeType]);
});
---
will alert the nodes in the desired order.
Very nice. There is one catch, though: after the very first node of the
document tree has been processed, action() is called with `null',


I can't see how. The "action" callback function is only ever called
with the value of the "element" parameter. It can only be called with
a null value if "element" is null. The recursive calls are only made
if "child" is not null, so they can't cause it, so "action" should
only be called with null arguemnt if someone calls traverseDFRTL with
a first parameter that is null.
and if the reference to the above function is passed as callback,
there will be a script error in the end ("null has no properties").
Could you post an example, including which browser causes it?
Furthermore, text nodes have no `tagName' property.
True. It was just a quick example.
It would also be nice if one could use the return value of traverseDFTRL() ,
What would the return value be? But yes, it could easily be turned into
a catemorphism (fold-like function):

function foldDOM(element , action) {
var childRets = [];
for(var child = element.lastChi ld; child; child = child.previousS ibling) {
childRets.push( foldDOM(child, action));
}
return action(element, childRets);
}

Then you could collect the innerText of a DOM element as:

foldDOM(element , function(node,c hildTexts) {
if (node.nodeType == 3) { // Text node
return node.nodeValue;
} else { // Element node
return childTexts.join ("");
}
});
and to allow for another identifier without modifying the function body.
If you want another name for it, treat the above as a function expression,
not a function declaration. Then you can call it whatever you like:

var myOwnName = function foldDOM(...){.. .foldDOM(...).. .};

I don't find the use of arguments.calle e particularly readable.
If a function can't know its own declared name inside its body,
what can it know?
Hence the following suggestion:

function isMethod(a)
{
var t;
return (a && ((t = typeof a) == "function" || t == "object"));
}

function traverseDFRTL(e lement, action)
{
for (var child = (element || document.docume ntElement).last Child;
child;
child = child.previousS ibling)
{
arguments.calle e(child, action);
}

if (isMethod(actio n))
{
return action(element) ;
If you insist on having a default argument, it should also apply here,
so,
return action(element || document.docume ntElement);
For default parameters, I prefer setting them at the top of the function,
i.e., as first line:
element = element || document.docume ntElement;
}
if you make this test (and I wouldn't - someone callig the function
should know that he is expected to pass a function as second argument),
I would at least fail if it's not:
else {
throw Error("Not a function as second argument!");
}

function(node)
{
if (node)


So here you depend on having the documentElement being traversed, but
null still being passed to the action function. That seems awfully
fragile and not very generic. I prefer to have a more generic, and
preferably simpler, function like my original traversal or the foldDOM
above, and then create a specialized function to call it with special
arguments that makes sense to the page it is used on.

/L
--
Lasse Reichstein Nielsen - lr*@hotpop.com
DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleD OM.html>
'Faith without judgement merely degrades the spirit divine.'
Apr 2 '06 #6
Lasse Reichstein Nielsen wrote:
de*******@gmail .com writes:
It's quite simple to walk to the DOM tree going forward however I can't
figure out a nice clean way to walk the DOM tree in reverse.


You want a depth-first right-to-left traversal of the nodes.
That sounds like a job for recursion:

---
function traverseDFRTL(e lement, action) {
for(var child = element.lastChi ld; child; child = child.previousS ibling) {
traverseDFRTL(c hild, action);
}
action(element) ;
}
---

Calling
---
traverseDFRTL(s omeElement, function(node) {
alert([node.tagName, node.nodeType]);
});
---
will alert the nodes in the desired order.
If you don't want recursion, you can do it iteratively as well, but it
will never be as simple.


<body onload="test()" ><table><tr><td >Help</td></tr></table>
<script type='text/javascript'>

function lessSimpleTrave rseDFRTL(elemen t, action) {
var next, prev=element.la stChild || element, lcOK=true;
for (; prev!=element; lcOK=prev.paren tNode!=(prev=ne xt)) {
if (lcOK && (next=prev.last Child)) continue; else action (prev);
next = prev.previousSi bling || prev.parentNode ; }
action(element) ; }

function test() {
lessSimpleTrave rseDFRTL(docume nt.body, function(node) {
alert(node.node Name+ "\n" + outerHTML(node) ); }); }

function outerHTML(elem) {
if (elem.outerHTML ) return elem.outerHTML;
var dup=elem.cloneN ode(true); //event handlers, some attribs omitted
var dummyDiv=elem.o wnerDocument.cr eateElement('DI V');
dummyDiv.append Child(dup);
var outer=dummyDiv. innerHTML;
delete dummyDiv;
return outer;
}
</script>
<div><span>Hi mom</span><p>Hi dad</p></div>
</body>
Tested with IE 6, FF 1.5.0.1,
Csaba Gabor from Vienna

Apr 3 '06 #7
Csaba Gabor wrote:
Lasse Reichstein Nielsen wrote:
de*******@gmail .com writes:
It's quite simple to walk to the DOM tree going forward however I can't
figure out a nice clean way to walk the DOM tree in reverse.


You want a depth-first right-to-left traversal of the nodes.
That sounds like a job for recursion:

---
function traverseDFRTL(e lement, action) {
for(var child = element.lastChi ld; child; child = child.previousS ibling) {
traverseDFRTL(c hild, action);
}
action(element) ;
}
---

Calling
---
traverseDFRTL(s omeElement, function(node) {
alert([node.tagName, node.nodeType]);
});
---
will alert the nodes in the desired order.
If you don't want recursion, you can do it iteratively as well, but it
will never be as simple.


<body onload="test()" ><table><tr><td >Help</td></tr></table>
<script type='text/javascript'>

function lessSimpleTrave rseDFRTL(elemen t, action) {
var next, prev=element.la stChild || element, lcOK=true;
for (; prev!=element; lcOK=prev.paren tNode!=(prev=ne xt)) {
if (lcOK && (next=prev.last Child)) continue; else action (prev);
next = prev.previousSi bling || prev.parentNode ; }
action(element) ; }


Kidding aside, the iterative version does offer something that the
recursive version doesn't because the recursive version only does
subtrees. The iterative version is happy to walk all the way back to
the root, from a given element. I found it quite instructive.

<body onload="test()" ><table><tr>< td id=td>Help</td></tr></table>
<script type='text/javascript'>

function backwalk(elem, action) {
// iterative reverse depth first traversal to root from elem
for (var next, lcOK=false; elem; lcOK=elem.paren tNode!=(elem=ne xt)) {
if (lcOK && (next=elem.last Child)) continue; else action (elem);
next = elem.previousSi bling || elem.parentNode ; } }

function test() {
backwalk(docume nt.getElementBy Id('td'), function(node) {
alert(node.node Name+ " (" + node.nodeType +
")\n" + outerHTML(node) ); }); }

function outerHTML(elem) {
if (elem.outerHTML ||elem.nodeType >8) return elem.outerHTML;
var dup=elem.cloneN ode(true);
var dummyDiv=elem.o wnerDocument.cr eateElement('DI V');
dummyDiv.append Child(dup);
var outer=dummyDiv. innerHTML;
delete dummyDiv;
return outer; }

</script>
<div><span>Hi mom</span><p id=p>Hi dad</p></div>
</body>
Plus the code is simpler. ;)
Csaba Gabor from Vienna

Apr 3 '06 #8
Lasse Reichstein Nielsen wrote:
Thomas 'PointedEars' Lahn <Po*********@we b.de> writes:
Lasse Reichstein Nielsen wrote:
---
function traverseDFRTL(e lement, action) {
for(var child = element.lastChi ld; child; child =
child.previousS ibling) {
traverseDFRTL(c hild, action);
}
action(element) ;
}
---

Calling
---
traverseDFRTL(s omeElement, function(node) {
alert([node.tagName, node.nodeType]);
});
---
will alert the nodes in the desired order.
Very nice. There is one catch, though: after the very first node of the
document tree has been processed, action() is called with `null',


I can't see how. The "action" callback function is only ever called
with the value of the "element" parameter. It can only be called with
a null value if "element" is null. The recursive calls are only made
if "child" is not null, so they can't cause it, so "action" should
only be called with null arguemnt if someone calls traverseDFRTL with
a first parameter that is null.


You are correct, the original code runs without error. It was in fact my
optimization which caused the problem. I passed `null' for triggering the
default value `document.docum entElement' and forgot that this would not
affect the value of `element' as passed to the second action() call.
It would also be nice [...] to allow for another identifier without
modifying the function body.


If you want another name for it, treat the above as a function expression,
not a function declaration. Then you can call it whatever you like:

var myOwnName = function foldDOM(...){.. .foldDOM(...).. .};

I don't find the use of arguments.calle e particularly readable.
If a function can't know its own declared name inside its body,
what can it know?


Think about it. That there are function expressions, i.e. functions
are first-level objects, is precisely why arguments.calle e is useful.
Hence the following suggestion:

function isMethod(a)
{
var t;
return (a && ((t = typeof a) == "function" || t == "object"));
}

function traverseDFRTL(e lement, action)
{
for (var child = (element || document.docume ntElement).last Child;
child;
child = child.previousS ibling)
{
arguments.calle e(child, action);
}

if (isMethod(actio n))
{
return action(element) ;


If you insist on having a default argument, it should also apply here,
so,
return action(element || document.docume ntElement);


In fact, it MUST then, unless one sets a requirement for checking
the argument of the callback in the callback. See above.
For default parameters, I prefer setting them at the top of the function,
i.e., as first line:
element = element || document.docume ntElement;


I prefer

if (!element) element = document.docume ntElement;

as it strikes me as being more efficient.
}


if you make this test (and I wouldn't - someone callig the function
should know that he is expected to pass a function as second argument),
I would at least fail if it's not:
else {
throw Error("Not a function as second argument!");
}


Agreed, although I would consider an alternative to a plain `throw'.
PointedEars
Apr 3 '06 #9
"Csaba Gabor" <da*****@gmail. com> writes:
function backwalk(elem, action) {
// iterative reverse depth first traversal to root from elem
for (var next, lcOK=false; elem; lcOK=elem.paren tNode!=(elem=ne xt)) {
if (lcOK && (next=elem.last Child)) continue; else action (elem);
next = elem.previousSi bling || elem.parentNode ; } }


I'm not entirely sure I read this correctly, but doesn't it follow
just a path through prevous siblings and parents from a note to the
root? It doesn't traverse children of the siblings, nor later
siblings at the same level.

An iterative function that works like the original traversal could be:
---
function leftMostInnerMo stDescendent(no de) {
while (node.hasChildN odes()) {
node = node.lastChild;
}
return node;
}

function traverse(elemen t, action) {
var next = leftMostInnerMo stDescendent(el ement);
do {
action(next);
if (next.previousS ibling) {
next = leftMostInnerMo stDescendent(ne xt.previousSibl ing);
} else {
next = next.parentNode ;
}
} while (next != element);
action(element) ;
}
---

A much more engenious (but probably dangerous) method is to use
rotation of the tree, putting the parent as the last child of its
first child and continuing that until the original shape is
restored (as it will be eventually). It's probably not doable in
general in a tree structure like HTML where not every node can
be a child of every other node. :)

/L
--
Lasse Reichstein Nielsen - lr*@hotpop.com
DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleD OM.html>
'Faith without judgement merely degrades the spirit divine.'
Apr 3 '06 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

9
2914
by: Marcello Pietrobon | last post by:
Hello, I am using Pyton 2.3 I desire to walk a directory without recursion this only partly works: def walk_files() : for root, dirs, files in os.walk(top, topdown=True): for filename in files: print( "file:" + os.path.join(root, filename) ) for dirname in dirs:
7
3651
by: pembed2003 | last post by:
Hi, I have a question about how to walk a binary tree. Suppose that I have this binary tree: 8 / \ 5 16 / \ / \ 3 7 9 22 / \ / \ / \
6
6464
by: Zri Man | last post by:
I'm relatively new to DB2 and was reasonably amused to see the REVERSE SCAN availability for Indexes. My assumptions are as follows: DB2/UDB uses B-Tree for indexing by default and is likely the main offering for Indexing within the DB. Reverse Scans could possibly only happen on the the leaf node of the index
7
4565
by: KraftDiner | last post by:
The os.walk function walks the operating systems directory tree. This seems to work, but I don't quite understand the tupple that is returned... Can someone explain please? for root, dirs, files in os.walk('/directory/'): print root # print dirs # print files
6
5920
by: Bruce | last post by:
Hi all, I have a question about traversing file systems, and could use some help. Because of directories with many files in them, os.walk appears to be rather slow. I`m thinking there is a potential for speed-up since I don`t need os.walk to report filenames of all the files in every directory it visits. Is there some clever way to use os.walk or another tool that would provide functionality like os.walk except for the listing of the...
8
2287
by: Andre Meyer | last post by:
Hi all os.walk() is a nice generator for performing actions on all files in a directory and subdirectories. However, how can one use os.walk() for walking through two hierarchies at once? I want to synchronise two directories (just backup for now), but cannot see how I can traverse a second one. I do this now with os.listdir() recursively, which works fine, but I am afraid that recursion can become inefficient for large hierarchies. ...
9
2876
by: silverburgh.meryl | last post by:
i am trying to use python to walk thru each subdirectory from a top directory. Here is my script: savedPagesDirectory = "/home/meryl/saved_pages/data" dir=open(savedPagesDirectory, 'r') for file in dir: if (isdir(file)): # get the full path of the file
4
1358
by: Joe Ardent | last post by:
Good day, everybody! From what I can tell from the archives, this is everyone's favorite method from the standard lib, and everyone loves answering questions about it. Right? :) Anyway, my question regards the way that the visit callback modifies the names list. Basically, my simple example is: ############################## def listUndottedDirs( d ): dots = re.compile( '\.' )
2
3019
by: Martin Marcher | last post by:
Hello, I'm playing around with os.walk and I made up del_tree(path) which I think is correct (in terms of the algorithm, but not as python wants it :)). As soon as some directory is deleted the iterator of os.walk chokes. OK that is somehow clear to me as it can't be valid anymore since it can't go to the just deleted directory but it is in the iterator.
0
10035
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
9984
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
8863
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7403
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5293
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5441
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
3949
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
3556
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
2811
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.