Reputation: 11
In jQuery to traverse DOM several steps upward instead of
$(this).parent().parent().parent().parent().parent().click();
I can write short version:
$(this).parent(5).click();
So, I want to know, if there's a way to shorten code instead of spamming '.parentNode'?
Upvotes: 0
Views: 1531
Reputation: 613
The sorcerer's one-liner... It's the same as @petermader's only shorter. I guess there's less of a need to be super explicit here, as this would probably be just an imported utility function, and it's still more reliable than a for loop.
const getParentElement = ({ parentNode }, n = 1) =>
Array.from({ length: n - 1 }, () => ({ parentNode } = parentNode)) && parentNode
Upvotes: 0
Reputation: 7285
Rather trivial:
function parent (element, n = 1) {
let {parentNode} = element;
for (let i = 1; parentNode && i < n; i++) {
({parentNode} = parentNode);
}
return parentNode;
}
const span = document.querySelector('span');
const directParent = parent(span); // direct parent node
const greatGreatGreatGrandParent = parent(span, 5); // parent node 5 times
console.log(directParent.getAttribute('data-foo')); // baz
console.log(greatGreatGreatGrandParent.getAttribute('data-foo')); // bar
<div data-foo="bar">
<div>
<div>
<div>
<div data-foo="baz">
<span>Hello, World!</span>
</div>
</div>
</div>
</div>
</div>
I check for parentNode
because it might be null
. In that case, I break the loop and return null
, because continuing the loop would result in an error:
({parentNode} = null); // TypeError: can't convert null to object
The parentheses are necessary to indicate that the opening {
doesn't start a block, but a destructuring assignment:
{parentNode} = parentNode; // SyntaxError: expected expression, got '='
I must admit that my solution is rather condensed and uses some new JavaScript features to make it even more succinct.
let {parentNode} = element;
mean?Object destructuring lets you read properties from an object in a more succinct way:
let {parentNode} = element;
is equivalent to:
let parentNode = element.parentNode;
As explained above,
({parentNode} = parentNode);
is equivalent to:
parentNode = parentNode.parentNode;
parentNode && i < n
exactly?Every for
-loop has a condition. If this condition is true, the loop is continued. If the condition is false, it breaks out of the loop.
I could write:
for (let i = 1; i < n; i++) {
// ...
}
That would run the loop n - 1
iterations. After the last iteration, i
is equal to n
, so the condition evaluates to false
and it stops, which is fine.
parentNode && i < n
extends this condition. It not only checks whether i
is less than n
, but it also checks if parentNode
contains a truthy value. If parentNode
is null
(which might happen is an element has no parent node), the loop will break, because it can't read the property parentNode
of null.
I hope you understand the explanations with the following function which is equivalent to the original one:
function parent (element, times) {
var n; // how many times 'parentNode'?
if (times) {
n = times;
} else {
n = 1;
}
var elementToReturn = element.parentNode;
for (var i = 1; i < n; i++) {
if (elementToReturn === null) {
break;
} else {
elementToReturn = elementToReturn.parentNode;
}
}
return elementToReturn;
}
Upvotes: 2