在JavaScript / jQuery中解析CSS

41

我正在尝试使用JavaScript实现CSS解析,以便实现以下目标:

a {
  color: red;
}

被解析为对象:

{
  'a' {
    'color': 'red'
  }
}

首先,是否有可以使用的JavaScript / jQuery

我的实现非常基础,所以我确定它绝非万无一失。例如,它对于基本CSS运作得很好,但对于类型为以下示例的属性:

background: url(data:image/png;base64, ....);

它失败了,因为我使用split(';')来分离property:value对。这里,;出现在value中,导致它也在那一点被分割。

有其他替代方法吗?

这是代码:

parseCSS: function(css) {
    var rules = {};
    css = this.removeComments(css);
    var blocks = css.split('}');
    blocks.pop();
    var len = blocks.length;
    for (var i = 0; i < len; i++)
    {
        var pair = blocks[i].split('{');
        rules[$.trim(pair[0])] = this.parseCSSBlock(pair[1]);
    }
    return rules;
},

parseCSSBlock: function(css) { 
    var rule = {};
    var declarations = css.split(';');
    declarations.pop();
    var len = declarations.length;
    for (var i = 0; i < len; i++)
    {
        var loc = declarations[i].indexOf(':');
        var property = $.trim(declarations[i].substring(0, loc));
        var value = $.trim(declarations[i].substring(loc + 1));

        if (property != "" && value != "")
            rule[property] = value;
    }
    return rule;
},

removeComments: function(css) {
    return css.replace(/\/\*(\r|\n|.)*\*\//g,"");
}

谢谢!


1
你为什么想这么做?你想达到什么目的?也许有另外一种(更简单)解决问题的方法。 - Pablo Fernandez
@Pablo 我尝试了很多办法,想避免解析CSS的需求,但不幸的是,我需要将规则存储在某个数据结构中。我的项目与此非常相配,因为它主要涉及基本的CSS规则(主要用例)。然而,由于存在一种使用情况可能需要解析任何CSS,所以最好能够做到万无一失。 - ankit
5个回答

100

您可以轻松使用浏览器自带的CSSOM解析CSS:

var rulesForCssText = function (styleContent) {
    var doc = document.implementation.createHTMLDocument(""),
        styleElement = document.createElement("style");

   styleElement.textContent = styleContent;
    // the style will only be parsed once it is added to a document
    doc.body.appendChild(styleElement);

    return styleElement.sheet.cssRules;
};

对于返回的每个规则,您可以查看rule.style中的属性。请参见http://jsfiddle.net/v2JsZ/以了解示例。


7
你太棒了!它运行得非常好! :) 我认为这是在浏览器中实现的最佳选择。你不需要任何库,而且比任何库都快,我想。 - Konstantin Smolyanin
2
使用这种方法有什么不足之处吗?如果这个方法可行,那么使用任何JS库来完成这样一个非平凡的任务还有什么意义呢? - Don Box
3
@DonBox: 嗯,正如我所说的:为了使用浏览器CSSOM,样式表必须是现有文档的一部分。这意味着最终用户将把样式作为他们正在查看的页面的一部分。对于某些情况来说,这不是最理想的选择。例如,您不能使用此技术来处理与当前用户正在查看的内容无直接关系的抽象样式表。 - kamelkev
1
存在一个缺陷,浏览器特定的规则被丢失了。 - useless
1
@kamelkev,样式元素未插入用户正在查看的文档中。它被插入到另一个文档中。 - GetFree
显示剩余8条评论

29

有一个用Javascript编写的CSS解析器,叫做JSCSSP


3
我之前确实看过它,但由于其太“笨重”了,所以我不想使用它。它有很多我不需要做的功能。 - ankit
10
@ankit: 那你在寻求什么? 如果你想要正确地解析CSS(也就是说,能够处理任意的CSS),那么你最终会得到一个“庞大”的库。否则,你可以继续使用你的轻量级实现,但需要知道它容易出错。 - josh3736
@josh3736 看起来你的评论给了我一个推动。我一直担心性能问题,但事实证明它运行得非常好! - ankit
我找不到一种使用JSCSSP从JSON对象解析回CSS的方法。 JSCSSP可以很好地将CSS解析为JSON,并且它使用的结构正是我们所需要的,但我们还需要一种将JSON对象解析回CSS的方法。 有什么想法/建议吗? - Norman
不要使用那个解析器,我用压缩后的“Bootstrap v3.3.2”源代码进行了测试,解析器只返回了25条规则。 - useless

16
为了编写最为完善的解析器,需按照规范中所定义的标记化和CSS语法的确切规则进行操作。请注意,您无需通过墨水实现规范。您可以从可能遇到的小部分和CSS开始,然后再扩展。更好的选择是跳过整个流程,并使用@Matthew的解决方案,除非这是一个学习练习。
有各种JavaScript词法扫描器和解析器生成器可供使用。整个语法都可以在w3的网站上找到。当您可以使用该语法和解析器生成器在JavaScript中生成解析器时,为何要重新工作呢?
  1. Jison
  2. Peg.js
  3. Cruiser.Parse
  4. McLexer
  5. JS/CC
CSS的生产规则如下。
stylesheet
  : [ CHARSET_SYM STRING ';' ]?
    [S|CDO|CDC]* [ import [ CDO S* | CDC S* ]* ]*
    [ [ ruleset | media | page ] [ CDO S* | CDC S* ]* ]*
  ;
import
  : IMPORT_SYM S*
    [STRING|URI] S* media_list? ';' S*
  ;
media
  : MEDIA_SYM S* media_list LBRACE S* ruleset* '}' S*
  ;
media_list
  : medium [ COMMA S* medium]*
  ;
medium
  : IDENT S*
  ;
page
  : PAGE_SYM S* pseudo_page?
    '{' S* declaration? [ ';' S* declaration? ]* '}' S*
  ;
pseudo_page
  : ':' IDENT S*
  ;
operator
  : '/' S* | ',' S*
  ;
combinator
  : '+' S*
  | '>' S*
  ;
unary_operator
  : '-' | '+'
  ;
property
  : IDENT S*
  ;
ruleset
  : selector [ ',' S* selector ]*
    '{' S* declaration? [ ';' S* declaration? ]* '}' S*
  ;
selector
  : simple_selector [ combinator selector | S+ [ combinator? selector ]? ]?
  ;
simple_selector
  : element_name [ HASH | class | attrib | pseudo ]*
  | [ HASH | class | attrib | pseudo ]+
  ;
class
  : '.' IDENT
  ;
element_name
  : IDENT | '*'
  ;
attrib
  : '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*
    [ IDENT | STRING ] S* ]? ']'
  ;
pseudo
  : ':' [ IDENT | FUNCTION S* [IDENT S*]? ')' ]
  ;
declaration
  : property ':' S* expr prio?
  ;
prio
  : IMPORTANT_SYM S*
  ;
expr
  : term [ operator? term ]*
  ;
term
  : unary_operator?
    [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* |
      TIME S* | FREQ S* ]
  | STRING S* | IDENT S* | URI S* | hexcolor | function
  ;
function
  : FUNCTION S* expr ')' S*
  ;
/*
 * There is a constraint on the color that it must
 * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
 * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
 */
hexcolor
  : HASH S*
  ;

这些生产规则对于CSS3已经过时了。例如,我没有看到'~'。 - huyz
1
你可以查看CoffeeScript,这是一个很好的文档完备的例子:http://jashkenas.github.com/coffee-script/documentation/docs/grammar.html - Brandon
如果有人看到这篇文章,就像@huyz所提到的那样,不仅答案已经过时(并非提问者的错),而且在目前阶段,CSS解析比任何人想象的都要复杂得多。 - Matthew Dean

2

这是一个简单的例子,尚未测试但应该可以工作,我在我的项目中使用类似的方法。

var div = jQuery('<div/>');
div[0].style = 'position:absolute;left:5px;top:10px;'; //Css to parse

div.css('left'); // => '5px'
div.css('top'); // => '10px'
div[0].style; // => Object containing all css

0

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