代码之家  ›  专栏  ›  技术社区  ›  jcubic

如何列出元素中的所有CSS变量名/值对

  •  7
  • jcubic  · 技术社区  · 6 年前

    我有JS库,我有一个问题:我正在创建一个临时元素,用monospace字体计算字符的大小。现在我正在复制inlie样式,但我需要原始样式中的所有样式,包括css变量。我不想克隆元素,因为里面有我不需要的元素。另外,元素可能有由用户设置的ID,不确定当有两个具有相同ID的元素时,这将如何工作,所以最好(我认为)将每个样式复制到新的临时元素。

    我有基于这些的代码:

    我的代码如下:

    function is_valid_style_property(key, value) {
        //checking that the property is not int index ( happens on some browser
        return typeof value === 'string' && value.length && value !== parseInt(value);
    }
    
    function copy_computed_style(from, to) {
        var computed_style_object = false;
        computed_style_object = from.currentStyle || document.defaultView.getComputedStyle(from, null);
    
        if (!computed_style_object) {
            return;
        }
        Object.keys(computed_style_object).forEach(function(key) {
            var value = computed_style_object.getPropertyValue(key);
            if (key.match(/^--/)) {
                console.log({key, value}); // this is never executed
            }
            if (is_valid_style_property(key, value)) {
                to.style.setProperty(key, value);
            }
        });
    }
    

    问题是 getComputedStyle ,不返回CSS变量。有没有其他解决方案来获取应用于元素的CSS变量列表?

    我需要CSS变量,因为我有应用于临时项内的元素(基于CSS变量)的CSS。克隆节点是将CSS变量从一个元素复制到另一个元素的唯一方法吗?

    编辑 :

    这不是重复的,因为CSS变量不仅可以在每个类的样式表中以内联方式设置。我的元素可以用我不知道的非常不同的css选择器添加样式。

    1 回复  |  直到 6 年前
        1
  •  1
  •   Temani Afif    6 年前

    基于这个答案 https://stackoverflow.com/a/37958301/8620333 我已经创建了一个依赖于 getMatchedCSSRules 以便检索所有CSS,然后提取CSS自定义属性。由于自定义属性是继承的,我们需要收集在元素中定义的属性和在任何父元素上定义的属性。

    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>

    这是代码的相关部分 :

    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]);
        }
    }
    

    如您所见,这将打印所需的值。您可以轻松地调整代码以将值存储在数组或对象中。

    1:这段代码没有优化,因为在某些情况下它可能会收集不需要的CSS。将继续编辑。