2010-07-24 12 views
25

Estoy tratando de poner en práctica el análisis de la CSS en JavaScript de forma que:de análisis de CSS en JavaScript/jQuery

a { 
    color: red; 
} 

se analiza en el objeto:

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

En primer lugar, ¿existe un JavaScript/jQuery biblioteca Puedo usar?

Mi implementación es bastante básica, así que estoy seguro de que no es infalible de ninguna manera. Por ejemplo, funciona bien para CSS básico, pero para una propiedad del tipo:

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

Fracasa porque estoy utilizando split(';') para separar property:value pares. Aquí, ; aparece en el value, por lo que se divide en ese punto también.

¿Hay alguna otra manera de hacerlo?

Aquí está el código:

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,""); 
} 

Gracias!

+1

¿Por qué quieres hacer eso? Qué estás intentando lograr. Tal vez haya otra forma (más simple) de resolver su problema –

+0

azul analizado en rojo ?!:) –

+0

@Pablo He intentado pensar en formas alternativas por las que pueda evitar la necesidad de analizar el CSS, pero desafortunadamente, necesito almacenar las reglas en alguna estructura de datos. Mi proyecto realmente funciona bastante bien con esto, ya que en su mayoría implica reglas básicas de CSS (caso de uso principal). Sin embargo, será bueno estar a prueba de tontos ya que hay un caso de uso donde puede necesitar analizar cualquier CSS. – ankit

Respuesta

27

Hay un analizador CSS escrita en Javascript llama JSCSSP

+3

Eché un vistazo antes, pero no quería usarlo, ya que es ** pesado **. Hace un montón de cosas que no necesito hacer – ankit

+9

@ankit: ¿Entonces qué estás pidiendo? Si quiere analizar * correctamente * (lo que significa que puede manejar cualquier CSS arbitrario), terminará con una biblioteca "pesada". De lo contrario, puede seguir con su implementación ligera sabiendo que es fácil de romper. – josh3736

+0

@ josh3736 Parece que su comentario fue el impulso que necesitaba. Estaba preocupado por los problemas de rendimiento, ¡pero resulta que funciona bastante bien! – ankit

10

Para escribir el analizador más infalible, siga las reglas exactas para tokenization y CSS grammar según lo definido en la especificación. Tenga en cuenta que no tiene que implementar la especificación por tinta. Puede comenzar con piezas pequeñas y CSS que probablemente encontrará, y luego expandir desde allí. Mejor aún, omita todo el proceso e implemente la solución de @ Matthew a menos que sea un ejercicio de aprendizaje.

Existen varios escáneres léxicos y generadores de analizadores disponibles para JavaScript. Toda la gramática está disponible en el sitio web de w3. ¿Por qué volver a trabajar cuando puede simplemente usar eso y los generadores del analizador para generar el analizador en JavaScript?

  1. Jison
  2. Peg.js
  3. Cruiser.Parse
  4. McLexer
  5. JS/CC

Las reglas de producción para CSS se dan a continuación.

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* 
    ; 
+0

Estas reglas de producción están desactualizadas para CSS3. No veo '~', por ejemplo, – huyz

+1

Puede ver CoffeeScript para obtener un buen ejemplo bien documentado: http://jashkenas.github.com/coffee-script/documentation/docs/grammar.html – Brandon

52

Usted puede utilizar fácilmente propia CSSOM del navegador para analizar 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; 
}; 

Para cada regla devuelto se puede ver en las propiedades en rule.style. Ver http://jsfiddle.net/v2JsZ/ para un ejemplo.

+3

¡Rock! ¡Funciona a las mil maravillas! :) Supongo que es la mejor opción para la implementación en el navegador. No necesitas ninguna biblioteca y es más rápido que cualquier biblioteca, creo. –

+0

He actualizado su jsfiddle con un ejemplo que solo analiza el CSS: http://jsfiddle.net/v2JsZ/85/ –

+0

¿Cómo es que si mis estilos tienen una imagen de fondo este código devuelve una ruta vacía: 'url()' ? – Moss