I'm trying to implement parsing of CSS in JavaScript so that:
a {
color: red;
}
is parsed into the object:
{
'a' {
'color': 'red'
}
}
First off,开发者_开发问答 is there a JavaScript / jQuery library I can use?
My implementation is pretty basic, so I'm sure it is not fool-proof by any means. For example, it works fine for basic CSS, but for a property of the type:
background: url(data:image/png;base64, ....);
It fails because I am using split(';')
to separate property:value
pairs. Here, ;
occurs in the value
, so it splits at that point too.
Is there an alternate way to do this?
Here is the code:
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,"");
}
Thanks!
You can easily use the Browser's own CSSOM to parse 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;
};
For each rule returned you can look at the properties in rule.style
. See http://jsfiddle.net/v2JsZ/ for an example.
There is a CSS parser written in Javascript called JSCSSP
To write the most fool-proof parser, follow the exact rules for tokenization and CSS grammar as defined in the spec. Note that you don't have to implement the spec by the ink. You can start with small parts and CSS that you will most likely encounter, and then expand from there. Even better, skip the entire process altogether and go with @Matthew's solution unless this is a learning exercise.
There are various lexical scanners and parser generators available for JavaScript. The entire grammar is available on w3's website. Why do the re-work when you can simply use that and the parser generators to generate the parser in JavaScript.
- Jison
- Peg.js
- Cruiser.Parse
- McLexer
- JS/CC
The production rules for CSS are given below.
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*
;
Simple example, not tested but should work, I'm using similar in my project.
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
Heres a parser from Tab Atkins that follows the spec strictly
https://github.com/tabatkins/parse-css
theres a pretty good video about how this parser works compared to others
精彩评论