Programmatic CSS analysis
Programmatic CSS is called a constructed CSSStyleSheet in the CSS Object Model (CSSOM) spec.
Find unused rules
The following snippet tries to find unused rules.
It does so by taking the first stylesheet, getting all the CSSStyleRule
s, then seeing if the selectorText
matches anything in the document.
[...document.styleSheets].flatMap(x => [...x.cssRules]).filter(e => e instanceof CSSStyleRule).filter(e => !document.querySelectorAll(e.selectorText).length)
Remove the “!” to find used rules:
[...document.styleSheets].flatMap(x => [...x.cssRules]).filter(e => e instanceof CSSStyleRule).filter(e => document.querySelectorAll(e.selectorText).length)
Issues to fix with it:
- Some rules will only match occasionally, maybe it’s more interesting to get a list of classes.
- Probably doesn’t traverse into layers or media queries.
- Can’t access cross-origin stylesheets
This version just looks at accessible rules:
[...document.styleSheets].flatMap(x => { try { return [...x.cssRules]; } catch (error) { return [];}}).filter(e => e instanceof CSSStyleRule).filter(e => document.querySelectorAll(e.selectorText).length)
Find rules with certain properties
Here are some snippets to find CSS rules that declare properties or have properties set to some value:
function cssRulesWithProperty(property) {
return [...document.styleSheets]
.flatMap(x => { try { return [...x.cssRules]; } catch (error) { return [];}})
.filter(e => e instanceof CSSStyleRule)
.filter(e => [...e.style].includes(property));
}
function cssRulesWithPropertyValue(property, value) {
return [...document.styleSheets]
.flatMap(x => { try { return [...x.cssRules]; } catch (error) { return [];}})
.filter(e => e instanceof CSSStyleRule)
.filter(e => [...e.style].includes(property) && e.style.getPropertyValue(property) == value);
}
You could use this to find CSS rules that make some elements sticky:
cssRulesWithPropertyValue("position", "sticky")
And here’s how you might try to remove that sticky property:
cssRulesWithPropertyValue("position", "sticky").forEach(rule => rule.style.removeProperty("position"))
This doesn’t always work, however (which I should fix).
Find all elements with certain properties
Here’s an example of how to find all position: sticky
elements:
[...document.querySelectorAll("*")].filter(el => getComputedStyle(el).getPropertyValue("position") == "sticky")
And here’s how you’d change them all to “static
”:
[...document.querySelectorAll("*")].filter(el => getComputedStyle(el).getPropertyValue("position") == "sticky").forEach(el => el.style.position = "static")
I’m not sure if there’s a more efficient way to find matching elements directly.
Listing classes?
Not sure that you can directly list classes without parsing .selectorText
yourself.
But I don’t think just making a regexp like “\b\.([^. ]+)\b
” (or whatever) would work since you can have dots in attribute selectors like “a[data-something*=".something"]
”.