Thomas 'PointedEars' Lahn said:
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.lastChild; child; child = child.previousSibling) {
childRets.push(foldDOM(child, action));
}
return action(element, childRets);
}
Then you could collect the innerText of a DOM element as:
foldDOM(element, function(node,childTexts) {
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.callee 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(element, action)
{
for (var child = (element || document.documentElement).lastChild;
child;
child = child.previousSibling)
{
arguments.callee(child, action);
}
if (isMethod(action))
{
return action(element);
If you insist on having a default argument, it should also apply here,
so,
return action(element || document.documentElement);
For default parameters, I prefer setting them at the top of the function,
i.e., as first line:
element = element || document.documentElement;
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