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
testtest
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.
Hi, Andrea,
ReplyDeleteI 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 :)
Hi Alihes, ElementTrasversal is a constructor, not an object, and has its prototyped methods (same Java Interface W3 proposal methods)
ReplyDeletemy 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? :-)
Thanks for the explanation.
ReplyDeleteYour nextElement and previousElement may not work properly on IE.
ReplyDeleteIn 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)
really interesting, thank You, I'll modify code as soon as I can :-)
ReplyDeleteHi Andrea,
ReplyDeleteas 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
I'll look for this bug but I tested my code and firstChild worked correctly
ReplyDeleteWhy limit it to element nodes? Why not let the user pass a parameter "element" or "text" for a different node type?
ReplyDelete