I'm working on a web-based system similar to https://visitortracking.com/, where different businesses can join as tenants. Once they join, they can create campaigns for their websites.
Before creating a campaign, they must first generate tracking scripts for their individual websites and embed these scripts into their sites. After that, they can create campaigns. While setting up a campaign, I want them to specify the elements on which a conversion will be counted.
To make this process easier, I'm creating a visual selector tool, where users can enter their website URL and visually select any element of their choice (e.g., "Add to Cart" or "Submit" buttons).
Since websites with complex structures make it difficult to identify HTML elements separately, I need a way to generate an accurate and unique CSS selector when an element is clicked.
I have created a function for this, but I'm not sure if it covers all edge cases. I would love to know if anyone has done similar work before or if there is a better alternative approach.
Keep in mind that these websites can be built using any tool (wordpress, custom html css, Shopify), so I don't have a way to share the exact HTML structure with you.
function generateQuerySelector(el) {
if (!el || el.tagName.toLowerCase() === 'html') return 'HTML';
const path = [];
while (el && el.nodeType === Node.ELEMENT_NODE) {
let selector = el.tagName.toLowerCase();
// Add ID if available
if (el.id) {
selector += '#' + el.id;
path.unshift(selector);
break; // IDs are unique, so we can stop here
}
// Add class names if available
if (el.className && typeof el.className === 'string') {
const classes = el.className.split(/\s+/).filter(Boolean);
if (classes.length > 0) {
selector += '.' + classes.join('.');
}
}
const parent = el.parentNode;
if (parent) {
const siblings = Array.from(parent.children).filter(
child => child.tagName.toLowerCase() === el.tagName.toLowerCase()
);
if (siblings.length > 1) {
const index = siblings.indexOf(el) + 1;
selector += `:nth-child(${index})`;
}
}
path.unshift(selector);
el = el.parentNode;
}
return path.join(' > ');
}
<>).generateQuerySelector()? As an alternative, you might be able to generate an xpathdata-guid=...then you can access that element via its data attribute in css with[data-guid='...']"IDs are unique, so we can stop here"- they should be unique but, as seen many, many times here, that rule is often ignored. In such cases how would your code fare?