Reputation: 949
I am trying to find a way to build the querySelector string of any given node. In other words - pick any node on a page - is it possible to walk up the DOM and build a string that would allow me to pass the generated string to document.querySelector and get back the node I chose?
From what I can tell querySelector has a bug where you can only use nth-child once in the string.
I have tried several times but so far have failed to find a solution. I want to do this in native JavaScript, not jQuery.Any suggestions?
Upvotes: 3
Views: 4694
Reputation: 1015
This works as long as DOM structure remains same. Even little change in HTML code could result to incorrect (in terms of return value) result of this function. So, it is not intended for long storing of DOM reference to HTML element.
function createQuerySelector(element) {
if (element.id) {
return `#${element.id}`;
}
const path = [];
let currentElement = element;
let error = false;
while (currentElement.tagName !== 'BODY') {
const parent = currentElement.parentElement;
if (!parent) {
error = true;
break;
}
const childTagCount= {};
let nthChildFound = false;
for (const child of parent.children) {
const tag = child.tagName;
const count = childTagCount[tag] || 0;
childTagCount[tag] = count + 1;
if (child === currentElement) {
nthChildFound = true;
break;
}
}
if (!nthChildFound) {
error = true;
break;
}
const count = childTagCount[currentElement.tagName];
const tag = currentElement.tagName.toLowerCase();
const selector = `${tag}:nth-of-type(${count})`;
path.push(selector);
currentElement = parent;
}
if (error) {
console.error(element);
throw new Error('Unable to create query selector');
}
path.push('body');
const querySelector = path.reverse().join(' > ');
return querySelector;
}
Upvotes: 0
Reputation: 189
I see this question is from 2015 but I just dealt with this issue and had to build a custom function to do so.
I've made a snippet to test it, just click any element and the querySelector should display as a string in the bottom div.
function getQuerySelector(elem) {
var element = elem;
var str = "";
function loop(element) {
// stop here = element has ID
if(element.getAttribute("id")) {
str = str.replace(/^/, " #" + element.getAttribute("id"));
str = str.replace(/\s/, "");
str = str.replace(/\s/g, " > ");
return str;
}
// stop here = element is body
if(document.body === element) {
str = str.replace(/^/, " body");
str = str.replace(/\s/, "");
str = str.replace(/\s/g, " > ");
return str;
}
// concat all classes in "queryselector" style
if(element.getAttribute("class")) {
var elemClasses = ".";
elemClasses += element.getAttribute("class");
elemClasses = elemClasses.replace(/\s/g, ".");
elemClasses = elemClasses.replace(/^/g, " ");
var classNth = "";
// check if element class is the unique child
var childrens = element.parentNode.children;
if(childrens.length < 2) {
return;
}
var similarClasses = [];
for(var i = 0; i < childrens.length; i++) {
if(element.getAttribute("class") ==
childrens[i].getAttribute("class")) {
similarClasses.push(childrens[i]);
}
}
if(similarClasses.length > 1) {
for(var j = 0; j < similarClasses.length; j++) {
if(element === similarClasses[j]) {
j++;
classNth = ":nth-of-type(" + j + ")";
break;
}
}
}
str = str.replace(/^/, elemClasses + classNth);
}
else{
// get nodeType
var name = element.nodeName;
name = name.toLowerCase();
var nodeNth = "";
var childrens = element.parentNode.children;
if(childrens.length > 2) {
var similarNodes = [];
for(var i = 0; i < childrens.length; i++) {
if(element.nodeName == childrens[i].nodeName) {
similarNodes.push(childrens[i]);
}
}
if(similarNodes.length > 1) {
for(var j = 0; j < similarNodes.length; j++) {
if(element === similarNodes[j]) {
j++;
nodeNth = ":nth-of-type(" + j + ")";
break;
}
}
}
}
str = str.replace(/^/, " " + name + nodeNth);
}
if(element.parentNode) {
loop(element.parentNode);
}
else {
str = str.replace(/\s/g, " > ");
str = str.replace(/\s/, "");
return str;
}
}
loop(element);
return str;
}
https://jsfiddle.net/wm6goeyw/
Upvotes: 6