How to list all css variables names/values pairs from element

后端 未结 1 700
面向向阳花
面向向阳花 2020-12-17 20:48

I have JS library and I have this issue: I\'m creating temporary element for calculating size of character using monospace font. Right now I\'m copying inlie style, but I ne

相关标签:
1条回答
  • 2020-12-17 21:29

    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>

    Here is the relevant part of the code1:

    var element = document.querySelector(".box");
    
    /*Get external styles*/
    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 styles*/
    var rules = element.getAttribute("style").trim().split(";");
    for(var j=0;j<rules.length;j++) {
        if(rules[j].trim().startsWith("--")) {
            console.log(rules[j]);
        }
    }
    

    As you can see, this will print the needed values. You can easily adjust the code to store the values in an array or an Object.

    1: This code is not optimized as it may gather non needed CSS in some cases. Will keep editing it.

    0 讨论(0)
提交回复
热议问题