Get the DOM path of the clicked <a>
Here is a solution for exact matching of an element.
It is important to understand that the selector (it is not a real one) that the chrome tools show do not uniquely identify an element in the DOM. (for example it will not distinguish between a list of consecutive span
elements. there is no positioning/indexing info)
An adaptation from a similar (about xpath) answer
$.fn.fullSelector = function () {
var path = this.parents().addBack();
var quickCss = path.get().map(function (item) {
var self = $(item),
id = item.id ? '#' + item.id : '',
clss = item.classList.length ? item.classList.toString().split(' ').map(function (c) {
return '.' + c;
}).join('') : '',
name = item.nodeName.toLowerCase(),
index = self.siblings(name).length ? ':nth-child(' + (self.index() + 1) + ')' : '';
if (name === 'html' || name === 'body') {
return name;
}
return name + index + id + clss;
}).join(' > ');
return quickCss;
};
And you can use it like this
console.log( $('some-selector').fullSelector() );
Demo at http://jsfiddle.net/gaby/zhnr198y/
I needed a native JS version, that returns CSS standard path (not jQuery), and deals with ShadowDOM. This code is a minor update on Michael Connor's answer, just in case someone else needs it:
function getDomPath(el) {
if (!el) {
return;
}
var stack = [];
var isShadow = false;
while (el.parentNode != null) {
// console.log(el.nodeName);
var sibCount = 0;
var sibIndex = 0;
// get sibling indexes
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 != '' ) { no id shortcuts, ids are not unique in shadowDom
// stack.unshift(el.nodeName.toLowerCase() + '#' + el.id);
// } else
var nodeName = el.nodeName.toLowerCase();
if (isShadow) {
nodeName += "::shadow";
isShadow = false;
}
if ( sibCount > 1 ) {
stack.unshift(nodeName + ':nth-of-type(' + (sibIndex + 1) + ')');
} else {
stack.unshift(nodeName);
}
el = el.parentNode;
if (el.nodeType === 11) { // for shadow dom, we
isShadow = true;
el = el.host;
}
}
stack.splice(0,1); // removes the html element
return stack.join(' > ');
}
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.
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
}