Users selects two or more elements in a HTML page. What i want to accomplish is to find those elements\' common ancestors (so body node would be the common ancestor if none
You can also use a DOM Range (when supported by the browser, of course). If you create a Range with the startContainer set to the earlier node in the document and the endContainer set to the later node in the document, then the commonAncestorContainer attribute of such a Range is the deepest common ancestor node.
Here is some code implementing this idea:
function getCommonAncestor(node1, node2) {
var dp = node1.compareDocumentPosition(node2);
// If the bitmask includes the DOCUMENT_POSITION_DISCONNECTED bit, 0x1, or the
// DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC bit, 0x20, then the order is implementation
// specific.
if (dp & (0x1 | 0x20)) {
if (node1 === node2) return node1;
var node1AndAncestors = [node1];
while ((node1 = node1.parentNode) != null) {
node1AndAncestors.push(node1);
}
var node2AndAncestors = [node2];
while ((node2 = node2.parentNode) != null) {
node2AndAncestors.push(node2);
}
var len1 = node1AndAncestors.length;
var len2 = node2AndAncestors.length;
// If the last element of the two arrays is not the same, then `node1' and `node2' do
// not share a common ancestor.
if (node1AndAncestors[len1 - 1] !== node2AndAncestors[len2 - 1]) {
return null;
}
var i = 1;
for (;;) {
if (node1AndAncestors[len1 - 1 - i] !== node2AndAncestors[len2 - 1 - i]) {
// assert node1AndAncestors[len1 - 1 - i - 1] === node2AndAncestors[len2 - 1 - i - 1];
return node1AndAncestors[len1 - 1 - i - 1];
}
++i;
}
// assert false;
throw "Shouldn't reach here!";
}
// "If the two nodes being compared are the same node, then no flags are set on the return."
// http://www.w3.org/TR/DOM-Level-3-Core/core.html#DocumentPosition
if (dp == 0) {
// assert node1 === node2;
return node1;
} else if (dp & 0x8) {
// assert node2.contains(node1);
return node2;
} else if (dp & 0x10) {
// assert node1.contains(node2);
return node1;
}
// In this case, `node2' precedes `node1'. Swap `node1' and `node2' so that `node1' precedes
// `node2'.
if (dp & 0x2) {
var tmp = node1;
node1 = node2;
node2 = tmp;
} else {
// assert dp & 0x4;
}
var range = node1.ownerDocument.createRange();
range.setStart(node1, 0);
range.setEnd(node2, 0);
return range.commonAncestorContainer;
}
The commonAncestorContainer property of the he Range API mentioned above, alongside its selectNode, makes this a no-brainer.
Run ("display") this code in Firefox's Scratchpad or a similar editor:
var range = document.createRange();
var nodes = [document.head, document.body]; // or any other set of nodes
nodes.forEach(range.selectNode, range);
range.commonAncestorContainer;
Note that both APIs are not supported by IE 8 or below.