Get the DOM path of the clicked <a>

HTML

<body>
<div class="lol">
<a class="rightArrow" href="javascriptVoid:(0);" title"Next image">
</div>
</body>

Pseudo Code

$(".rightArrow").click(function() {
rightArrowParents = this.dom(); //.dom(); is the pseudo function ... it should show the whole
alert(rightArrowParents);
});

Alert message would be:

body div.lol a.rightArrow

How can I get this with javascript/jquery?


Here is a native JS version that returns a jQuery path. I'm also adding IDs for elements if they have them. This would give you the opportunity to do the shortest path if you see an id in the array.

var path = getDomPath(element);
console.log(path.join(' > '));

Outputs

body > section:eq(0) > div:eq(3) > section#content > section#firehose > div#firehoselist > article#firehose-46813651 > header > h2 > span#title-46813651

Here is the function.

function getDomPath(el) {
  var stack = [];
  while ( el.parentNode != null ) {
    console.log(el.nodeName);
    var sibCount = 0;
    var sibIndex = 0;
    for ( var i = 0; i < el.parentNode.childNodes.length; i++ ) {
      var sib = el.parentNode.childNodes[i];
      if ( sib.nodeName == el.nodeName ) {
        if ( sib === el ) {
          sibIndex = sibCount;
        }
        sibCount++;
      }
    }
    if ( el.hasAttribute('id') && el.id != '' ) {
      stack.unshift(el.nodeName.toLowerCase() + '#' + el.id);
    } else if ( sibCount > 1 ) {
      stack.unshift(el.nodeName.toLowerCase() + ':eq(' + sibIndex + ')');
    } else {
      stack.unshift(el.nodeName.toLowerCase());
    }
    el = el.parentNode;
  }

  return stack.slice(1); // removes the html element
}

Using jQuery, like this (followed by a solution that doesn't use jQuery except for the event; lots fewer function calls, if that's important):

$(".rightArrow").click(function() {
  var rightArrowParents = [];
  $(this).parents().addBack().not('html').each(function() {
    var entry = this.tagName.toLowerCase();
    if (this.className) {
      entry += "." + this.className.replace(/ /g, '.');
    }
    rightArrowParents.push(entry);
  });
  alert(rightArrowParents.join(" "));
  return false;
});

Live example:

$(".rightArrow").click(function() {
  var rightArrowParents = [];
  $(this).parents().addBack().not('html').each(function() {
    var entry = this.tagName.toLowerCase();
    if (this.className) {
      entry += "." + this.className.replace(/ /g, '.');
    }
    rightArrowParents.push(entry);
  });
  alert(rightArrowParents.join(" "));
  return false;
});
<div class="lol multi">
  <a href="#" class="rightArrow" title="Next image">Click here</a>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

(In the live examples, I've updated the class attribute on the div to be lol multi to demonstrate handling multiple classes.)

That uses parents to get the ancestors of the element that was clicked, removes the html element from that via not (since you started at body), then loops through creating entries for each parent and pushing them on an array. Then we use addBack to add the a back into the set, which also changes the order of the set to what you wanted (parents is special, it gives you the parents in the reverse of the order you wanted, but then addBAck puts it back in DOM order). Then it uses Array#join to create the space-delimited string.

When creating the entry, if there's anything on className we replace spaces with . to support elements that have more than one class (<p class='foo bar'> has className = "foo bar", so that entry ends up being p.foo.bar).

Just for completeness, this is one of those places where jQuery may be overkill, you can readily do this just by walking up the DOM:

$(".rightArrow").click(function() {
  var rightArrowParents = [],
    elm,
    entry;

  for (elm = this; elm; elm = elm.parentNode) {
    entry = elm.tagName.toLowerCase();
    if (entry === "html") {
      break;
    }
    if (elm.className) {
      entry += "." + elm.className.replace(/ /g, '.');
    }
    rightArrowParents.push(entry);
  }
  rightArrowParents.reverse();
  alert(rightArrowParents.join(" "));
  return false;
});

Live example:

$(".rightArrow").click(function() {
  var rightArrowParents = [],
    elm,
    entry;

  for (elm = this; elm; elm = elm.parentNode) {
    entry = elm.tagName.toLowerCase();
    if (entry === "html") {
      break;
    }
    if (elm.className) {
      entry += "." + elm.className.replace(/ /g, '.');
    }
    rightArrowParents.push(entry);
  }
  rightArrowParents.reverse();
  alert(rightArrowParents.join(" "));
  return false;
});
<div class="lol multi">
  <a href="#" class="rightArrow" title="Next image">Click here</a>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

There we just use the standard parentNode property of the element repeatedly to walk up the tree until either we run out of parents or we see the html element. Then we reverse our array (since it's backward to the output you wanted), and join it, and we're good to go.