How to list all css variables names/values pairs from element
Based on this answer https://stackoverflow.com/a/37958301/8620333 I have created a code that rely on getMatchedCSSRules
in order to retrieve all the CSS and then extract the CSS custom properties. Since Custom properties are inherited we need to gather the one defined within the element and the one defined on any parent element.
if (typeof window.getMatchedCSSRules !== 'function') { var ELEMENT_RE = /[\w-]+/g, ID_RE = /#[\w-]+/g, CLASS_RE = /\.[\w-]+/g, ATTR_RE = /\[[^\]]+\]/g, // :not() pseudo-class does not add to specificity, but its content does as if it was outside it PSEUDO_CLASSES_RE = /\:(?!not)[\w-]+(\(.*\))?/g, PSEUDO_ELEMENTS_RE = /\:\:?(after|before|first-letter|first-line|selection)/g; // convert an array-like object to array function toArray(list) { return [].slice.call(list); }
// handles extraction of `cssRules` as an `Array` from a stylesheet or something that behaves the same function getSheetRules(stylesheet) { var sheet_media = stylesheet.media && stylesheet.media.mediaText; // if this sheet is disabled skip it if ( stylesheet.disabled ) return []; // if this sheet's media is specified and doesn't match the viewport then skip it if ( sheet_media && sheet_media.length && ! window.matchMedia(sheet_media).matches ) return []; // get the style rules of this sheet return toArray(stylesheet.cssRules); }
function _find(string, re) { var matches = string.match(re); return matches ? matches.length : 0; }
// calculates the specificity of a given `selector` function calculateScore(selector) { var score = [0,0,0], parts = selector.split(' '), part, match; //TODO: clean the ':not' part since the last ELEMENT_RE will pick it up while (part = parts.shift(), typeof part == 'string') { // find all pseudo-elements match = _find(part, PSEUDO_ELEMENTS_RE); score[2] += match; // and remove them match && (part = part.replace(PSEUDO_ELEMENTS_RE, '')); // find all pseudo-classes match = _find(part, PSEUDO_CLASSES_RE); score[1] += match; // and remove them match && (part = part.replace(PSEUDO_CLASSES_RE, '')); // find all attributes match = _find(part, ATTR_RE); score[1] += match; // and remove them match && (part = part.replace(ATTR_RE, '')); // find all IDs match = _find(part, ID_RE); score[0] += match; // and remove them match && (part = part.replace(ID_RE, '')); // find all classes match = _find(part, CLASS_RE); score[1] += match; // and remove them match && (part = part.replace(CLASS_RE, '')); // find all elements score[2] += _find(part, ELEMENT_RE); } return parseInt(score.join(''), 10); }
// returns the heights possible specificity score an element can get from a give rule's selectorText function getSpecificityScore(element, selector_text) { var selectors = selector_text.split(','), selector, score, result = 0; while (selector = selectors.shift()) { if (matchesSelector(element, selector)) { score = calculateScore(selector); result = score > result ? score : result; } } return result; }
function sortBySpecificity(element, rules) { // comparing function that sorts CSSStyleRules according to specificity of their `selectorText` function compareSpecificity (a, b) { return getSpecificityScore(element, b.selectorText) - getSpecificityScore(element, a.selectorText); }
return rules.sort(compareSpecificity); }
// Find correct matchesSelector impl function matchesSelector(el, selector) { var matcher = el.matchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector || el.msMatchesSelector; return matcher.call(el, selector); }
//TODO: not supporting 2nd argument for selecting pseudo elements //TODO: not supporting 3rd argument for checking author style sheets only window.getMatchedCSSRules = function (element /*, pseudo, author_only*/) { var style_sheets, sheet, sheet_media, rules, rule, result = []; // get stylesheets and convert to a regular Array style_sheets = toArray(window.document.styleSheets);
// assuming the browser hands us stylesheets in order of appearance // we iterate them from the beginning to follow proper cascade order while (sheet = style_sheets.shift()) { // get the style rules of this sheet rules = getSheetRules(sheet); // loop the rules in order of appearance while (rule = rules.shift()) { // if this is an @import rule if (rule.styleSheet) { // insert the imported stylesheet's rules at the beginning of this stylesheet's rules rules = getSheetRules(rule.styleSheet).concat(rules); // and skip this rule continue; } // if there's no stylesheet attribute BUT there IS a media attribute it's a media rule else if (rule.media) { // insert the contained rules of this media rule to the beginning of this stylesheet's rules rules = getSheetRules(rule).concat(rules); // and skip it continue }
// check if this element matches this rule's selector if (matchesSelector(element, rule.selectorText)) { // push the rule to the results set result.push(rule); } } } // sort according to specificity return sortBySpecificity(element, result); };}
var element = document.querySelector(".box");
/*Get element style*/var obj = window.getMatchedCSSRules(element)[0];var all_css = obj.parentStyleSheet.cssRules;for(var i=0;i < all_css.length;i++) { var rules = all_css[i].cssText.substring(all_css[i].cssText.indexOf("{")+1,all_css[i].cssText.indexOf("}")); rules = rules.split(";"); for(var j=0;j<rules.length;j++) { if(rules[j].trim().startsWith("--")) { console.log(rules[j]); } }}/*get inline style*/var rules = element.getAttribute("style").trim().split(";");for(var j=0;j<rules.length;j++) { if(rules[j].trim().startsWith("--")) { console.log(rules[j]); }}
:root { --b: 20px;}
.box { background: red; height: 100px; --c: blue; border: 1px solid var(--c);}.element { --e:30px; padding:var(--e);}
<div class="box element" style="color:blue;--d:10ch;border-radius:20px;"></div>
Accessing a CSS custom property (aka CSS variable) through JavaScript
You can use document.body.style.setProperty('--name', value);
:
var bodyStyles = window.getComputedStyle(document.body);
var fooBar = bodyStyles.getPropertyValue('--foo-bar'); //get
document.body.style.setProperty('--foo-bar', newValue);//set
Get an overview of all css variables set on root
Found the answer thanks to a post on SO. It reads the :root
property from the stylesheet and loops through the properties looking for the --
keyword.
I wasn't a fan of all the filter function chaining, so I'll leave my own solution here.
/**
* Get all simpleResponse CSS variables on the root of the chat widget.
*/
function getCustomizableProperties(): string[] {
// Find the css sheet that includes the simpleResponse CSS settings.
const simpleResponseStylesheet = getSimpleResponseStyleSheet();
if (!simpleResponseStylesheet) {
console.debug('No customizable properties found. Skipping custom theme rendering.')
return [];
}
// Once found, collect the CSS settings and put them into an array.
const properties = getSimpleResponseStyleProperties(simpleResponseStylesheet);
return properties;
}
function getSimpleResponseStyleSheet(): CSSStyleSheet | undefined {
const styleSheets = Array.from(document.styleSheets);
const simpleResponseStylesheet = styleSheets.find(styleSheet => {
if (styleSheet.href === null) {
const cssRules = Array.from(styleSheet.cssRules);
return cssRules.find(rule => rule.cssText.includes('--simpleResponse'))
}
return undefined;
});
return simpleResponseStylesheet;
}
function getSimpleResponseStyleProperties(styleSheet: CSSStyleSheet): string[] {
const cssRules = Array.from(styleSheet.cssRules);
// Casting to any to access properties missing from typing.
const rootStyleRule: any = cssRules.find((cssRule) => {
const rule = cssRule as any;
return rule.selectorText === ':root';
})
const rootStyleProperties = Array.from(rootStyleRule.style) as string[];
return rootStyleProperties.filter(prop => prop.includes('--simpleResponse'));
}
This returns an array of CSS variables
0: "--simpleResponse-background-color"
1: "--simpleResponse-text-color"
2: "--simpleResponse-text-font"
3: "--simpleResponse-text-font-weight"
4: "--simpleResponse-text-font-size"
Get all CSS properties for a class or id with Javascript/JQuery
Use document#styleSheets and extract all rules from all stylesheets into array. Then filter the array by the selectorText
.
Note: I've used a simple Array#includes to check if the requested selector appears in selectorText
, but you might want to create a stricter check to prevent false positives. For example the selector text .demo
can find rules for .demogorgon
as well.
const findClassRules = (selector, stylesheet) => { // combine all rules from all stylesheets to a single array const allRules = stylesheet !== undefined ? Array.from((document.styleSheets[stylesheet] || {}).cssRules || []) : [].concat(...Array.from(document.styleSheets).map(({ cssRules }) => Array.from(cssRules))); // filter the rules by their selectorText return allRules.filter(({ selectorText }) => selectorText && selectorText.includes(selector)); };
console.log(findClassRules('.demo', 0));
.demo { color: red;}
.demo::before { content: 'cats';}
Related Topics
Bootstrap 3 Dropdown on iPad Not Working
How to Read/Parse Individual Transform Style Values in JavaScript
How to Set Mousemove Update Speed
Infinite Scrolling with React Js
How to Disable JavaScript Function Calls from the Browser Console
What Cross-Browser Issues Have You Faced
D3 Bar Graph Example Not Working Locally
How to Do Anything About "Repaints on Scroll" Warning in Chrome for "Overflow:Scroll" Div
How to Add a List of Images to the Document from an Array of Urls
Chrome Extension Inject Sidebar into Page
Find Selected Item in Datalist in HTML
How to Set a JavaScript Onclick Event to a Class with CSS
Getelementbyclass().Setattribute Doesn't Work
JavaScript - Smooth Parallax Scrolling with Mouse Wheel
Modify the Value of Each Textfield Based on Original Value Using Jquery
JavaScript Hide/Show Div on Checkbox: Checked/Unchecked