Reputation: 11922
I was looking for a function that would return the 'distance' between elements in the DOM in terms of ancestors, siblings and descendants. For example, suppose I had:
<div id="div1">
<div id="div5"></div>
</div>
<div id="div2">
<div id="div6">
<div id="div9"></div>
</div>
<div id="div7"></div>
</div>
<div id="div3"></div>
<div id="div4">
<div id="div8">
<div id="div10"></div>
</div>
</div>
then I'd want a function that would return the distance between #div5
and #div10
as something like:
{
up: 1,
across: 3,
down: 2
}
Since to get from #div5
to #div10
you have to go up one generation, forward 3 siblings (to #div4
) and then down 2 generations.
Similarly, #div9
to #div1
would return:
{
up: 2,
across: -1,
down: 0
}
for going up two generations, and then back one sibling.
I've already got a function that does this (which I'll include as an answer below) so I'm including it here because a) I thought someone else might find it useful; and b) maybe someone else has a better way of doing it.
Upvotes: 0
Views: 702
Reputation: 11922
Ok, here's what I have for it. I've hopefully explained it well enough in the code comments:
function DOMdistance(elem1,elem2) {
if (elem1 === elem2) {
return {
up: 0,
across: 0,
down: 0
};
}
var parents1 = [elem1],
parents2 = [elem2],
gens = 1,
sibs = 0,
sibElem;
// searches up the DOM from elem1 to the body, stopping and
// returning if it finds elem2 as a direct ancestor
while (elem1 !== document.body) {
elem1 = elem1.parentNode;
if (elem1 === elem2) {
return {
up: parents1.length,
across: 0,
down: 0
};
}
parents1.unshift(elem1);
}
// reset value of elem1 for use in the while loop that follows:
elem1 = parents1[parents1.length - 1];
// searches up the DOM from elem2 to the body, stopping and
// returning if it finds elem1 as a direct ancestor
while (elem2 !== document.body) {
elem2 = elem2.parentNode;
if (elem2 === elem1) {
return {
up: 0,
across: 0,
down: parents2.length
};
}
parents2.unshift(elem2);
}
// finds generation depth from body of first generation of ancestors
// of elem1 and elem2 that aren't common to both
while (parents1[gens] === parents2[gens]) {
gens++;
}
sibElem = parents1[gens];
// searches forward across siblings from the earliest non-common ancestor
// of elem1, looking for earliest non-common ancestor of elem2
while (sibElem) {
sibElem = sibElem.nextSibling;
if (sibElem && sibElem.tagName) {
sibs++;
if (sibElem === parents2[gens]) {
return {
up: parents1.length - gens - 1,
across: sibs,
down: parents2.length - gens - 1
};
}
}
}
sibs = 0;
sibElem = parents1[gens];
// searches backward across siblings from the earliest non-common ancestor
// of elem1, looking for earliest non-common ancestor of elem2
while (sibElem) {
sibElem = sibElem.previousSibling;
if (sibElem && sibElem.tagName) {
sibs--;
if (sibElem === parents2[gens]) {
return {
up: parents1.length - gens - 1,
across: sibs,
down: parents2.length - gens - 1
};
}
}
}
}
So, for example, getting the 'distance' from #div5
to #div10
in the DOM described in the question would use something like:
var divOne = document.getElementById('div5'),
divTwo = document.getElementById('div10'),
distance = DOMdistance(divOne, divTwo);
and therefore distance
would be:
{
up: 1,
across: 3,
down: 2
}
demo: http://jsfiddle.net/x58Ga/
Upvotes: 2