Преобразование текста CSS в объект JavaScript

У меня есть следующий текст в виде строки 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)';
};

Любые идеи или уже созданные скрипты?

Ответ 1

Это начало парсера, который может делать то, что вы хотите. Конечно, это требует работы, особенно если вы хотите обрабатывать любые общие 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 + '";');
    }

Ответ 2

Ответ 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 (string) в функцию:

.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).
  • Включает массовую модификацию существующих CSSStyleDeclarations (например, document.body.style), используя один звонок Object.assign(document.body.style, parseCSSText(cssText).style).
  • Не выполняется, когда имя свойства приходит без значения (запись без двоеточия) и даже наоборот.
  • Обновление 2017-09-28: Обрабатывает новые строки также в именах правил, сбрасывает пробелы.
  • Обновление 2017-09-28: обрабатывает комментарии (/*...*/).

Quirks:

  • Если последнее объявление 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>

Ответ 3

Если документ 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 и т.д....

Ответ 4

У меня такая же проблема, что и 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 для объекта JavaScript