Reputation: 29407
I mean an array of them. That is a chain from top HTML to destination element including the element itself.
for example for element <A>
it would be:
[HTML, BODY, DIV, DIV, P, SPAN, A]
Upvotes: 42
Views: 57170
Reputation: 11159
You can walk the chain of element.parentNode
s until you reach an falsey value, appending to an array as you go:
const getParents = el => {
for (var parents = []; el; el = el.parentNode) {
parents.push(el);
}
return parents;
};
const el = document.querySelector("b");
console.log(getParents(el).reverse().map(e => e.nodeName));
<div><p><span><b>Foo</b></span></div>
Note that reversing is done in the caller because it's not essential to the lineage algorithm. Mapping to e.nodeName
is purely for presentation and also non-essential.
Note that this approach means you'll wind up with the document element as the last element in the chain. If you don't want that, you can add && el !== document
to the loop stopping condition.
The overall time complexity of the code above is linear and reverse()
is in-place, so it doesn't require an extra allocation. unshift
in a loop, as some of the other answers recommend, is quadratic and may harm scalability on uncommonly-deep DOM trees in exchange for a negligible gain in elegance.
Upvotes: 5
Reputation: 8063
get all parent nodes of child in javascript array
let selectedTxtElement = document.getElementById("target");
let els = [];
while (selectedTxtElement) {
els.unshift(selectedTxtElement);
selectedTxtElement = selectedTxtElement.parentNode;
}
Upvotes: -2
Reputation: 424
I like this method:
[...(function*(e){do { yield e; } while (e = e.parentNode);})($0)]
... where $0 is your element.
An upside of this method is that it can be used as a value in expressions.
To get an array without the target element:
[...(function*(e){while (e = e.parentNode) { yield e; }})($0)]
Upvotes: 10
Reputation: 681
Another alternative (based on this):
for(var e = document.getElementById("target"),p = [];e && e !== document;e = e.parentNode)
p.push(e);
Upvotes: 2
Reputation: 5341
I believe this will likely be the most performant in the long run in the most scenarios if you are making frequent usage of this function. The reason for why t will be more performant is because it initially checks to see what kind of depths of ancestry it might encounter. Also, instead of creating a new array every time you call it, this function will instead efficiently reuse the same array, and slice it which is very optimized in some browsers. However, since there is no really efficient way I know of to check the maximum depth, I am left with a less efficient query-selector check.
// !IMPORTANT! When moving this coding snippet over to your production code,
// do not run the following depthtest more than once, it is not very performant
var kCurSelector="*|*", curDepth=3;
while (document.body.querySelector(kCurSelector += '>*|*')) curDepth++;
curDepth = Math.pow(2, Math.ceil(Math.log2(startDepth))),
var parentsTMP = new Array(curDepth);
function getAllParentNodes(Ele){
var curPos = curDepth;
if (Ele instanceof Node)
while (Ele !== document){
if (curPos === 0){
curPos += curDepth;
parentsTMP.length <<= 1;
parentsTMP.copyWithin(curDepth, 0, curDepth);
curDepth <<= 1;
}
parentsTMP[--curPos] = Ele;
Ele = Ele.parentNode;
}
return retArray.slice(curPos)
}
The browser compatibility for the above function is that it will work in Edge, but not in IE. If you want IE support, then you will need a Array.prototype.copyWithin
polyfill.
Upvotes: 0
Reputation: 66693
You can try something like:
var nodes = [];
var element = document.getElementById('yourelement');
nodes.push(element);
while(element.parentNode) {
nodes.unshift(element.parentNode);
element = element.parentNode;
}
Upvotes: 26
Reputation: 60424
A little shorter (and safer, since target
may not be found):
var a = document.getElementById("target");
var els = [];
while (a) {
els.unshift(a);
a = a.parentNode;
}
Upvotes: 67