Skip to content

Instantly share code, notes, and snippets.

@lifeart
Created November 2, 2023 11:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lifeart/6a6ceb08ee131b6c14918e04d824bf06 to your computer and use it in GitHub Desktop.
Save lifeart/6a6ceb08ee131b6c14918e04d824bf06 to your computer and use it in GitHub Desktop.
resolve item selector on page with list of items
var classes = {};
var cnt = 1;
function hashForClass(rawClassName) {
const className = rawClassName.trim();
if (className in classes) {
classes[className].cnt++;
return classes[className].index;
} else {
classes[className] = {
index: cnt,
cnt: 1,
};
cnt++;
return classes[className].index;
}
}
function generateSelectors(node, selector = '') {
// Base case: if the node is not an element or doesn't have a parent, return an empty array
if (!node || node.nodeType !== 1 || !node.parentElement) return [];
if (
node.tagName === 'SVG' ||
node.tagName === 'SCRIPT' ||
node.tagName === 'NOSCRIPT' ||
node.tagName === 'STYLE'
)
return [];
if (typeof node.className !== 'string') {
return [];
}
// Generate a selector for the current node
const nodeName = node.nodeName.toLowerCase();
const idSelector = node.id ? `#${node.id}` : '';
const classSelectors = node.className
.split(' ')
.sort()
.map((cls) => (cls ? `.${hashForClass(cls)}` : ''))
.join('');
const currentSelector = `${nodeName}${idSelector}${classSelectors}`;
// Build the complete selector for the current node
const completeSelector = selector ? selector + ' > ' + currentSelector : currentSelector;
// Generate selectors for the child nodes
const childSelectors = Array.from(node.children).flatMap((child) =>
generateSelectors(child, completeSelector),
);
// Return the selector for the current node, along with the selectors for the child nodes
return [completeSelector].concat(childSelectors);
}
// Usage:
const selectors = generateSelectors(document.body);
function frequencyMapForSelectors(selectors) {
const frequencyMap = {};
for (const selector of selectors) {
if (selector in frequencyMap) {
frequencyMap[selector].count += 1;
} else {
const nestedChildren = selectors.filter((k) => k.startsWith(selector) && k !== selector);
frequencyMap[selector] = {
count: 1,
depth: selector.split('>').length,
children: nestedChildren.length,
};
}
}
let selectorWithMaxChildren = '';
let maxChildren = 0;
Object.keys(frequencyMap).forEach((key) => {
const ref = frequencyMap[key];
if (ref.children < 1) {
delete frequencyMap[key];
}
if (ref.count <= 2) {
delete frequencyMap[key];
}
});
Object.keys(frequencyMap).forEach((key) => {
const ref = frequencyMap[key];
if (ref.children > maxChildren) {
maxChildren = ref.children;
selectorWithMaxChildren = key;
}
});
const selector = selectorWithMaxChildren
.split('>')
.pop()
.split('.')
.map((cls) => {
const c = parseInt(cls);
return Object.keys(classes).find((key) => classes[key].index === parseInt(c));
})
.filter((cls) => cls !== undefined)
.join('.');
return '.' + selector;
}
console.log(frequencyMapForSelectors(selectors));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment