dementialover
dementialover

Reputation: 11

Javascript traversing DOM upwards

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

Answers (2)

Andrea
Andrea

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

PeterMader
PeterMader

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 '='

Edit:

I must admit that my solution is rather condensed and uses some new JavaScript features to make it even more succinct.

What does 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;

What does 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

Related Questions