Parsing CSS in JavaScript / jQuery
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