My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Monday, July 30, 2007

JavaScript ElementTraversal proposal

27 July W3 announced a new Working Draft, called: ElementTraversal

This is a "new" feature but today it's not cross browser so this is my proposal to have Element Trasversal with every recent browser:

(ElementTraversal = function(node){
this.node = node;
this.firstElementChild = this.getFirstElementChild();
this.lastElementChild = this.getLastElementChild();
this.previousElementSibling = this.getPreviousElementSibling();
this.nextElementSibling = this.getNextElementSibling();
this.childElementCount = this.getChildElementCount();

}).extend(null, {
getFirstElementChild:function(){
for(var c = this.node.childNodes,
i = 0,
j = (c && c.length) || 0,
e;
i < j;
i++
){
if(c[i].nodeType === 1)
e = c[j = i];
};
return e;
},
getLastElementChild:function(){
for(var c = this.node.childNodes,
i = ((c && c.length) || 0) - 1,
j = -1,
e;
i > j;
i--
){
if(c[i].nodeType === 1)
e = c[j = i];
};
return e;
},
getPreviousElementSibling:function(){
var e = this.node.previousSibling;
while(e && e.nodeType !== 1)
e = e.previousSibling;
return e;
},
getNextElementSibling:function(){
var e = this.node.nextSibling;
while(e && e.nodeType !== 1)
e = e.nextSibling;
return e;
},
getChildElementCount:function(){
for(var c = this.node.childNodes,
i = 0,
j = (c && c.length) || 0,
n = 0;
i < j;
i++
){
if(c[i].nodeType === 1)
++n;
};
return n;
},
update:function(){
this.constructor.call(this, this.node);
return this;
}
});


And this is a basic test:


test


test

test
test

test




My proposal is based on extend Function prototype, You can find them here.

Just a note about update extra method: not every browser support a defineGetter / setter method so update is a quick way to have correct properties.

If You create an instance and You modify some node, You should use:

ET.update();

This method returns instance itself, so You can pass them directly on other functions.

8 comments:

Alihes said...

Hi, Andrea,

I loved your extend posts, but I guess I must have missed something. Why create an empty ElementTraversal object that you immediately extend with the methods it's supposed to have? Why not just create the object and its methods at the same time?

Other than that, I like your proposal :)

Andrea Giammarchi said...

Hi Alihes, ElementTrasversal is a constructor, not an object, and has its prototyped methods (same Java Interface W3 proposal methods)

my extend prototype is both for Object and Function, in this case I used Function.prototype.extemd

(function(){}).extend

to assign simply and quickly each method so every new ElementTrasversal instance will have its properties, same assigned inside constructor, and inherited prototyped methods (so JavaScript doesn't need to create newfunctins each instance, a correct behaviour if You don't need "private" closures).

Is my proposal more clear now? :-)

Alejandro Moreno said...

Thanks for the explanation.

medyk said...

Your nextElement and previousElement may not work properly on IE.
In in IE comment nodes has nodeType equal to 1 as well.
The best universal check to see if node is of element type is: Boolean(node.attributes)

Andrea Giammarchi said...

really interesting, thank You, I'll modify code as soon as I can :-)

russel said...

Hi Andrea,

as I understand how the DOM works, it is faster during execution if you code your functions like so (this example is for firstElementChild):

var child = this.node.firstChild;
while(child && child.nodeType != 1)
child = child.nextSibling;
return child || null;

Also I think you have a bug in getFirstElementChild - there is no break in the loop so it will actually return the last child, no?

Cheers,

Russel

Andrea Giammarchi said...

I'll look for this bug but I tested my code and firstChild worked correctly

Bill said...

Why limit it to element nodes? Why not let the user pass a parameter "element" or "text" for a different node type?