将CSS文本转换为JavaScript对象

19

我有一个JavaScript字符串,内容如下:

.mybox {
 display: block; 
 width: 20px; 
 height: 20px;
 background-color: rgb(204, 204, 204);
 }

我想要将其转换为JavaScript对象

var mybox = {
 'display': 'block',
 'width': '20px',
 'height': '20px';
 'background-color': 'rgb(204, 204, 204)';
};

有任何想法或现成的脚本吗?


3
您介意告诉我们为什么要这样做吗?也许您想达到的目标可以通过更干净的代码来实现。 - Tadeck
1
你的目标是什么?你需要以面向对象的方式处理CSS声明吗?你需要如何处理calc()rgba()等函数符号?你想要如何生成变量名?对于选择器div > section:first-child ~ p:hover,变量名会是什么样子? - Anders Marzi Tornblad
@atornblad 获取CSS属性对象很简单。不管属性是什么,该属性的值都将是一个字符串。 - Omar Abid
你只需要一个用JavaScript编写的CSS解析器。换句话说,你想要做的事情就是解析器所做的事情。例如,这个 - Pointy
1
我想你在JS领域中指的是backgroundColor而不是background-color,对吗? :) - Alan H.
显示剩余5条评论
4个回答

15

2017年答案

function parseCSSText(cssText) {
    var cssTxt = cssText.replace(/\/\*(.|\s)*?\*\//g, " ").replace(/\s+/g, " ");
    var style = {}, [,ruleName,rule] = cssTxt.match(/ ?(.*?) ?{([^}]*)}/)||[,,cssTxt];
    var cssToJs = s => s.replace(/\W+\w/g, match => match.slice(-1).toUpperCase());
    var properties = rule.split(";").map(o => o.split(":").map(x => x && x.trim()));
    for (var [property, value] of properties) style[cssToJs(property)] = value;
    return {cssText, ruleName, style};
} /* updated 2017-09-28 */

描述

将以下cssText(字符串)传递给函数:

.mybox {
     display: block; 
     width: 20px; 
     height: 20px;
     background-color: rgb(204, 204, 204);
 }

...将会返回以下对象:

{   cssText: ... /* the original string including new lines, tabs and spaces */, 
    ruleName: ".mybox",
    style: {
        "": undefined,
        display: "block",
        width: "20px",
        height: "20px",
        backgroundColor: "rgb(204, 204, 204)"
     }
}

用户还可以传递包含cssText的内容,例如:
display: block; width: 20px; height: 20px; background-color: rgb(204, 204, 204);

特点:
  • 既可与CSSRule.cssText一起使用,也可与CSSStyleDeclaration.cssText一起使用。
  • 将CSS属性名称(background-color)转换为JS属性名称(backgroundColor)。它甚至处理非常不规则的名称,例如back%gr- -ound---color: red;(转换为backGrOundColor)。
  • 通过单个调用Object.assign(document.body.style, parseCSSText(cssText).style),使现有CSSStyleDeclarations(如document.body.style)进行大规模修改。
  • 当属性名称没有值(没有冒号的条目)时,也不会失败,反之亦然。
  • 更新于2017-09-28:在规则名称中也处理换行符,折叠空格。
  • 更新于2017-09-28:处理注释(/*...*/)。

特点:

  • 如果规则中的最后一个CSS声明以分号结尾,则返回的样式将包括一个空名称的属性""和一个undefined值,反映了分号后面的空字符串。我认为这是正确的行为。
  • 如果属性值(字符串文字)包括冒号或分号或CSS注释,例如div :: before {content:'test:test2; / * test3 * /';},则该函数将返回错误结果。我不知道如何避免这种情况。
  • 目前,它会将带有前缀的属性名(例如-somebrowser-someproperty)不正确地转换为SomebrowserSomeproperty而不是somebrowserSomeproperty。我想找到一种解决方法,既不会破坏代码的简洁性,又能够达到目的。

实时示例

function parseCSSText(cssText) {
    var cssTxt = cssText.replace(/\/\*(.|\s)*?\*\//g, " ").replace(/\s+/g, " ");
    var style = {}, [,ruleName,rule] = cssTxt.match(/ ?(.*?) ?{([^}]*)}/)||[,,cssTxt];
    var cssToJs = s => s.replace(/\W+\w/g, match => match.slice(-1).toUpperCase());
    var properties = rule.split(";").map(o => o.split(":").map(x => x && x.trim()));
    for (var [property, value] of properties) style[cssToJs(property)] = value;
    return {cssText, ruleName, style};
} /* updated 2017-09-28 */

Example:
    var sty = document.getElementById("mystyle");
    var out = document.getElementById("outcome");
    var styRule = parseCSSText(sty.innerHTML);
    var outRule = parseCSSText(out.style.cssText);
    out.innerHTML = 
        "<b>⦁ CSS in #mystyle</b>: " + JSON.stringify(styRule) + "<br>" +
        "<b>⦁ CSS of #outcome</b>: " + JSON.stringify(outRule);
    console.log(styRule, outRule); /* Inspect result in the console. */
<style id="mystyle">
.mybox1, /* a comment
    and new lines 
    to step up the game */
    .mybox 
{
    display: block; 
    width: 20px; height: 20px;
    background-color: /* a comment
        and a new line */ 
        rgb(204, 204, 204);
    -somebrowser-someproperty: somevalue;
}
</style>

<div id="outcome" style="
    display: block; padding: 0.5em;
    background-color: rgb(144, 224, 224);
">...</div>

<b style="color: red;">Also inspect the browser console.</b>


嘿!@7vujy0f0hy,请问我该如何编辑这个程序以便一次性转换整个CSS文件,例如:.taxi { background-color: #F8F8F8; color: #999; } #car { color: blue; } #user{ color: blue; height: 20px; } - tony pro
@tonypro:好问题。虽然不是万无一失,但以下表达式将返回每个规则(.taxi#car#user)的结果数组:cssText.match(/[^}]*{[^}]*}/g).map(parseCSSText)(其中 cssText 是您的字符串)。您还可以在该表达式后附加 .reduce((m,r) => ({...m, [r.ruleName]: r.style}), {}),以将该数组转换为另一个更整洁的数据结构。对于回答晚了,我很抱歉。 - 7vujy0f0hy
这个代码运行良好,但会抛出一个警告:数组中间有意外的逗号 no-sparse-arrays。 - Daniel Lefebvre
@DanielLefebvre:两种解决方案。1. 在您的ESLint中禁用no-sparse-arrays2. 或者在我的代码中将类似var [, ruleName, rule]的结构替换为var [undefined, ruleName, rule][,, cssTxt]替换为[undefined, undefined, cssTxt] - 7vujy0f0hy
解决注释问题的方法是在预处理步骤中将其删除,例如使用以下正则表达式:s = s.replace(/\/\*[^*]*\*\//g, ''); - Waruyama

6
这是一个可能可以满足您需求的解析器的开头。当然,它需要进一步优化,尤其是如果您想处理提供的任何通用CSS。这假设输入的CSS按照您提供的方式编写,第一行是属性名称,最后一行是“}”等等。
如果您不仅仅想处理基本属性,编写复杂的解析器并不容易。例如,如果您声明了以下内容:
input[type="text"],
table > tr:nth-child(2),
#link a:hover {
    -webkit-transition: width 2s; /* Safari and Chrome */
}

这是有效的CSS,但是你如何从中提取一个有效的JavaScript变量名称? 如何将-webkit-transition转换为有意义的属性名称? 整个任务看起来像是你在“完全错误”的情况下完成的。与其致力于编写解析器,不如一开始就寻找更稳定的解决方案。

顺便说一下,这是你可以开始的代码:

    var s = '.mybox {\n';
    s += 'display: block;\n';
    s += 'width: 20px;\n';
    s += 'height: 20px;\n';
    s += 'background-color: rgb(204, 204, 204);\n';
    s += '}\n';

    // split css by line
    var css_rows = s.split('\n'); 

    // filter out empty elements and strip ';'      
    css_rows = css_rows.filter(function(x){ return x != '' }).map(function(x){ return x.trim().replace(';', '') });

    // create object
    var json_name = css_rows[0].trim().replace(/[\.\{\ \#]/g, '');
    eval('var ' + json_name + ' = {};');
    // remove first and last element
    css_rows = css_rows.splice(1, css_rows.length-2)

    for (elem in css_rows)
    {
        var elem_parts = css_rows[elem].split(':');
        var property_name = elem_parts[0].trim().replace('-', '');
        var property_value = elem_parts[1].trim();
        eval(json_name + '.' + property_name + ' = "' + property_value + '";');
    }

1
如果CSS文档被包含在HTML文档中,以便样式声明实际上被加载,那么您可以通过以下方式在Javascript中遍历所有样式:
// Get all style sheet documents in this html document
var allSheets = document.styleSheets;

for (var i = 0; i < allSheets.length; ++i) {
    var sheet = allSheets[i];

    // Get all CSS rules in the current style sheet document
    var rules = sheet.cssRules || sheet.rules;
    for (var j = 0; j < rules.length; ++j) {
        var rule = rules[j];

        // Get the selector definition ("div > p:first-child" for example)
        var selector = rule.selectorText;

        // Create an empty object to put the style definitions in
        var result = {};

        var style = rule.style;
        for (var key in style) {
            if (style.hasOwnProperty(key)) {
                result[key] = style.cssText;
            }
        }

        // At this point, you have the selector in the
        // selector variable (".mybox" for example)

        // You also have a javascript object in the
        // result variable, containing what you need.

        // If you need to output this as json, there
        // are several options for this.
    }
}

如果这不是你想要的,比如你想解析CSS文档并创建JavaScript源文件,那么你需要研究词法分析器、CSS文档对象模型、JSON序列化等相关内容。

0

我在使用LeafLet时遇到了同样的问题,尝试将CSS样式与JavaScript代码分离...最终得到了这个:

var css = {};

for (var i = 0; i < document.styleSheets.length; ++i) {
    var sheet = document.styleSheets[i];
    for (var j = 0; j < sheet.cssRules.length; ++j) {
        var rule = sheet.cssRules[j];

        var cssText = rule.cssText.slice(rule.cssText.indexOf('{')+1);
        var attrs = cssText.split(';');

        var ruleSet = {};
        for (var k = 0; k < attrs.length; ++k) {
            var keyValue = attrs[k].split(':');
            if (keyValue.length == 2) {
                var key = keyValue[0].trim();
                var value = keyValue[1].trim();
                ruleSet[key] = value;
            }
        }

        for (var testRule in ruleSet) { // We are going to add the rule iff it is not an empty object
            css[rule.selectorText] = ruleSet;
            break;
        }
    }
}

console.log(css);

这将会产生类似于这样的结果:

css to JavaScript object


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接