2018 年更新:这个问题早在PostCSS存在之前就被问到了,我可能会使用它。
我想将一堆 CSS 解析成 AST,这样我就可以为某些 CSS 指令添加前缀。
是否有用于 JavaScript 或 Node 的 CSS 解析器来执行此操作?
我已经搜索过 NPM。我发现的唯一有用的结果是parser-lib,但它是基于流的,看起来我需要为每个 CSS 节点编写自己的发射器。
更新:我也找到了 JCSSP,但它没有文档......
2018 年更新:这个问题早在PostCSS存在之前就被问到了,我可能会使用它。
我想将一堆 CSS 解析成 AST,这样我就可以为某些 CSS 指令添加前缀。
是否有用于 JavaScript 或 Node 的 CSS 解析器来执行此操作?
我已经搜索过 NPM。我发现的唯一有用的结果是parser-lib,但它是基于流的,看起来我需要为每个 CSS 节点编写自己的发射器。
更新:我也找到了 JCSSP,但它没有文档......
更新:我之前提到过 JCSSP,它似乎被放弃了。显然,NPM 上的css 模块是最好的:
css = require 'css'
input = '''
body {
font-family: sans-serif;
}
#thing.foo p.bar {
font-weight: bold;
}
'''
obj = css.parse input
sheet = obj.stylesheet
for rule in sheet.rules
rule.selectors = ('#XXX ' + s for s in rule.selectors)
console.log css.stringify(obj)
输出:
#XXX body {
font-family: sans-serif;
}
#XXX #thing.foo p.bar {
font-weight: bold;
}
另外值得一提的是LESS。虽然它主要是对 CSS 的(很棒的)扩展,但 LESS 解析器确实让您可以访问 AST。
纯 CSS 样式表也是有效的 LESS 样式表,因此您可以从现有的内容开始并轻松使用 LESS 的扩展。
这是我们的开源 CSS 解析器css.js
这是一个简单的解析示例:
<script type="text/javascript">
var cssString = ' .someSelector { margin:40px 10px; padding:5px}';
//initialize parser object
var parser = new cssjs();
//parse css string
var parsed = parser.parseCSS(cssString);
console.log(parsed);
</script>
编辑后将解析的数据结构字符串化为 CSS 字符串
var newCSSString = parser.getCSSForEditor(parsed);
我们的 CSS 解析器的主要特点是:
无需使用外部 css 解析器,我们可以使用原生 css 解析器
var sheetRef=document.getElementsByTagName("style")[0];
console.log("----------------list of rules--------------");
for (var i=0; i<sheetRef.sheet.cssRules.length; i++){
var sheet = sheetRef.sheet ? sheetRef.sheet : sheetRef.styleSheet;
if (sheet.cssRules.length > 0) {
//console.log(sheet.cssRules[i]);
console.log(sheet.cssRules[i].selectorText);
console.log(sheet.cssRules[i].cssText);
}}
.red{
color:red;
}
插入规则
var sheetRef=document.getElementsByTagName("style")[0];
var sheet = sheetRef.sheet ? sheetRef.sheet : sheetRef.styleSheet;
sheet.insertRule('.foo{color:red;}', 0);
删除规则 所有浏览器,版本 9 之前的 IE 除外
var sheetRef=document.getElementsByTagName("style")[0];
var sheet = sheetRef.sheet ? sheetRef.sheet : sheetRef.styleSheet;
sheet.removeRule (0);
删除规则 所有浏览器,版本 9 之前的 IE 除外
var sheetRef=document.getElementsByTagName("style")[0];
var sheet = sheetRef.sheet ? sheetRef.sheet : sheetRef.styleSheet;
sheet.deleteRule (0);
添加媒体
function AddScreenMedia () {
var styleTag = document.getElementsByTagName("style")[0];
// the style sheet in the style tag
var sheet = styleTag.sheet ? styleTag.sheet : styleTag.styleSheet;
if (sheet.cssRules) { // all browsers, except IE before version 9
var rule = sheet.cssRules[0];
var mediaList = rule.media;
alert ("The media types before adding the screen media type: " + mediaList.mediaText);
mediaList.appendMedium ("screen");
alert ("The media types after adding the screen media type: " + mediaList.mediaText);
}
else { // Internet Explorer before version 9
// note: the rules collection does not contain the at-rules
alert ("Your browser does not support this example!");
}
}
@media print {
body {
font-size: 13px;
color: #FF0000;
}
}
some text
<button onclick="AddScreenMedia ();">Add screen media</button>
获取规则
var sheetRef=document.getElementsByTagName("style")[0];
console.log("----------------list of rules--------------");
for (var i=0; i<sheetRef.sheet.cssRules.length; i++){
var sheet = sheetRef.sheet ? sheetRef.sheet : sheetRef.styleSheet;
if (sheet.cssRules.length > 0) {
//console.log(sheet.cssRules[i]);
console.log(sheet.cssRules[i].selectorText);
console.log(sheet.cssRules[i].cssText);
console.log(sheet.cssRules[i].style.color)
console.log(sheet.cssRules[i].style.background)
console.log(sheet.cssRules[i].style)
}}
.red{
color:red;
background:orange;
}
<h1>red</h1>
我最终使用了这个对于我的实现来说足够轻的库(在Kemal Dağ
的答案中提供)。其他选项对于我所追求的客户端实现来说太重了。
https://github.com/jotform/css.js
a paid nerd
的原始答案在我点击媒体查询之前效果很好。
我不得不添加一些递归,这就是我最终得到的。
请原谅我的 TypeScript。
private scopeCSS(css: string): CSS.Stylesheet {
let ast: CSS.Stylesheet = CSS.parse(css);
let stylesheet: CSS.StyleRules|undefined = ast.stylesheet;
if (stylesheet) {
let rules: Array<CSS.Rule|CSS.Media> = stylesheet.rules;
let prefix = `[data-id='sticky-container-${this.parent.id}']`;
// Append our container scope to rules
// Recursive rule appender
let ruleAppend = (rules: Array<CSS.Rule|CSS.Media>) => {
rules.forEach(rule => {
let cssRule = <CSS.Rule>rule;
let mediaRule = <CSS.Media>rule;
if (cssRule.selectors !== undefined) {
cssRule.selectors = cssRule.selectors.map(selector => `${prefix} ${selector}`);
}
if (mediaRule.rules !== undefined) {
ruleAppend(mediaRule.rules);
}
});
};
ruleAppend(rules);
}
return ast;
}
function scopeCSS(css, prefix) {
var ast = CSS.parse(css);
var stylesheet = ast.stylesheet;
if (stylesheet) {
var rules = stylesheet.rules;
// Append our container scope to rules
// Recursive rule appender
var ruleAppend = function(rules) {
rules.forEach(function(rule) {
if (rule.selectors !== undefined) {
rule.selectors = rule.selectors.map(function(selector) {
return prefix + " " + selector;
});
}
if (rule.rules !== undefined) {
ruleAppend(rule.rules);
}
});
};
ruleAppend(rules);
}
return ast;
}