Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A JavaScript Implementation of TreeWalker
var NodeFilter = {
FILTER_ACCEPT: 1,
FILTER_REJECT: 2,
FILTER_SKIP: 3,
SHOW_ALL: -1,
SHOW_ELEMENT: 1,
SHOW_ATTRIBUTE: 2,
SHOW_TEXT: 4,
SHOW_CDATA_SECTION: 8,
SHOW_ENTITY_REFERENCE: 16,
SHOW_ENTITY: 32,
SHOW_PROCESSING_INSTRUCTIONS: 64,
SHOW_COMMENT: 128,
SHOW_DOCUMENT: 256,
SHOW_DOCUMENT_TYPE: 512,
SHOW_DOCUMENT_FRAGMENT: 1024,
SHOW_NOTATION: 2048
};
var TreeWalker = function (root, whatToShow, filter, expandEntityReferences) {
this.root = root;
this.whatToShow = whatToShow;
this.filter = filter;
this.expandEntityReferences = expandEntityReferences;
this.currentNode = root;
this.NodeFilter = NodeFilter;
};
TreeWalker.prototype.parentNode = function () {
var testNode = this.currentNode;
do {
if (
testNode !== this.root &&
testNode.parentNode &&
testNode.parentNode !== this.root
) {
testNode = testNode.parentNode;
} else {
return null;
}
} while (this._getFilteredStatus(testNode) !== this.NodeFilter.FILTER_ACCEPT);
(testNode) && (this.currentNode = testNode);
return testNode;
};
TreeWalker.prototype.firstChild = function () {
var testNode = this.currentNode.firstChild;
while(testNode) {
if(this._getFilteredStatus(testNode) === this.NodeFilter.FILTER_ACCEPT) {
break;
}
testNode = testNode.nextSibling;
}
(testNode) && (this.currentNode = testNode);
return testNode;
};
TreeWalker.prototype.lastChild = function () {
var testNode = this.currentNode.lastChild;
while (testNode) {
if(this._getFilteredStatus(testNode) === this.NodeFilter.FILTER_ACCEPT) {
break;
}
testNode = testNode.previousSibling;
}
(testNode) && (this.currentNode = testNode);
return testNode;
};
TreeWalker.prototype.nextNode = function () {
var testNode = this.currentNode;
while (testNode) {
if (testNode.childNodes.length !== 0) {
testNode = testNode.firstChild;
} else if (testNode.nextSibling) {
testNode = testNode.nextSibling;
} else {
while (testNode) {
if (testNode.parentNode && testNode.parentNode !== this.root) {
if (testNode.parentNode.nextSibling) {
testNode = testNode.parentNode.nextSibling;
break;
} else {
testNode = testNode.parentNode;
}
}
else return null;
}
}
if (testNode && this._getFilteredStatus(testNode) === this.NodeFilter.FILTER_ACCEPT) {
break;
}
}
(testNode) && (this.currentNode = testNode);
return testNode;
};
TreeWalker.prototype.previousNode = function () {
var testNode = this.currentNode;
while (testNode) {
if (testNode.previousSibling) {
testNode = testNode.previousSibling;
while (testNode.lastChild) {
testNode = testNode.lastChild;
}
}
else {
if (testNode.parentNode && testNode.parentNode !== this.root) {
testNode = testNode.parentNode;
}
else testNode = null;
}
if (testNode && this._getFilteredStatus(testNode) === this.NodeFilter.FILTER_ACCEPT) {
break;
}
}
(testNode) && (this.currentNode = testNode);
return testNode;
};
TreeWalker.prototype.nextSibling = function () {
var testNode = this.currentNode;
while(testNode) {
(testNode.nextSibling) && (testNode = testNode.nextSibling);
if(this._getFilteredStatus(testNode) === this.NodeFilter.FILTER_ACCEPT) {
break;
}
}
(testNode) && (this.currentNode = testNode);
return testNode;
};
TreeWalker.prototype.previousSibling = function () {
var testNode = this.currentNode;
while(testNode) {
(testNode.previousSibling) && (testNode = testNode.previousSibling);
if(this._getFilteredStatus(testNode) == this.NodeFilter.FILTER_ACCEPT) {
break;
}
}
(testNode) && (this.currentNode = testNode);
return testNode;
};
TreeWalker.prototype._getFilteredStatus = function (node) {
var mask = ({
/* ELEMENT_NODE */ 1: this.NodeFilter.SHOW_ELEMENT,
/* ATTRIBUTE_NODE */ 2: this.NodeFilter.SHOW_ATTRIBUTE,
/* TEXT_NODE */ 3: this.NodeFilter.SHOW_TEXT,
/* CDATA_SECTION_NODE */ 4: this.NodeFilter.SHOW_CDATA_SECTION,
/* ENTITY_REFERENCE_NODE */ 5: this.NodeFilter.SHOW_ENTITY_REFERENCE,
/* ENTITY_NODE */ 6: this.NodeFilter.SHOW_PROCESSING_INSTRUCTION,
/* PROCESSING_INSTRUCTION_NODE */ 7: this.NodeFilter.SHOW_PROCESSING_INSTRUCTION,
/* COMMENT_NODE */ 8: this.NodeFilter.SHOW_COMMENT,
/* DOCUMENT_NODE */ 9: this.NodeFilter.SHOW_DOCUMENT,
/* DOCUMENT_TYPE_NODE */ 10: this.NodeFilter.SHOW_DOCUMENT_TYPE,
/* DOCUMENT_FRAGMENT_NODE */ 11: this.NodeFilter.SHOW_DOCUMENT_FRAGMENT,
/* NOTATION_NODE */ 12: this.NodeFilter.SHOW_NOTATION
})[node.nodeType];
return (
(mask && (this.whatToShow & mask) == 0) ?
this.NodeFilter.FILTER_REJECT :
(this.filter && this.filter.acceptNode) ?
this.filter.acceptNode(node) :
this.NodeFilter.FILTER_ACCEPT
);
};
if (!document.createTreeWalker) {
document.createTreeWalker = function (root, whatToShow, filter, expandEntityReferences) {
return new TreeWalker(root, whatToShow, filter, expandEntityReferences);
};
}
@dbalatero
Copy link

dbalatero commented Sep 6, 2011

Is this code free to use (under MIT)? I've been using this on an app, and I'd like to make sure our licenses are all in order.

@Krinkle
Copy link

Krinkle commented Dec 19, 2012

This implementation appears to be lacking support for FILTER_SKIP. The Filter is supposed to return FILTER_SKIP instead of FILTER_REJECT. The behaviour is quite different (skipping is reject the node, but considering its children).

@mems
Copy link

mems commented Sep 9, 2016

@dbalatero It's look like it's based on Mozile source code which has a "MPL 1.1/GPL 2.0/LGPL 2.1" licence

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment