PostCSS:如何根据当前规则中的声明有条件地添加新规则?
PostCSS: How to conditionally add a new rule based on declaration in current rule?
你好,我一直在尝试 PostCSS,并且在编写一个插件时遇到了一些困难,该插件在发现某些 CSS 属性时会附加一条新规则。
我想要达到的目标...
开始 CSS:
.selector {
color: red;
not-yet-standard-property-a: 10px;
not-yet-standard-property-b: 20px;
}
PostCSS插件后:
.selector {
color: red;
not-yet-standard-property-a: 10px;
not-yet-standard-property-b: 20px;
}
.ie .selector {
standard-property-a: 10px;
standard-property-b: 20px;
}
每当我看到其中一个时,我很容易添加新规则not-yet-standard-property-*
…
return function (css) {
css.walkRules(function (rule) {
rule.walkDecls('not-yet-standard-property-a', function (decl) {
css.insertAfter(rule, '.ie ' + rule.selector + '{ standard-property-a: '+ decl.value +' }');
});
rule.walkDecls('not-yet-standard-property-b', function (decl) {
css.insertAfter(rule, '.ie ' + rule.selector + '{ standard-property-b: '+ decl.value +' }');
});
rule.walkDecls('not-yet-standard-property-c', function (decl) {
css.insertAfter(rule, '.ie ' + rule.selector + '{ standard-property-c: '+ decl.value +' }');
});
});
}
但是输出并不理想……
.selector {
color: red;
not-yet-standard-property-a: 10px;
not-yet-standard-property-b: 20px;
not-yet-standard-property-c: 53px;
}
.ie .selector {
standard-property-c: 53px;
}
.ie .selector {
standard-property-b: 20px;
}
.ie .selector {
standard-property-a: 10px;
}
理想情况下,新规则只会在遍历整个规则后添加一次,但 PostCSS 似乎不允许在 walkRules 函数中使用条件,所以我不确定如何阻止它为它看到的每个规则创建新规则。
我已经链接到上述代码的演示。任何关于更好架构的建议将不胜感激,就像我之前说过的,我对此很陌生。谢谢!
Demo
编辑:已解决。非常感谢 Andrey Sitnik!我已将他的回答标记为正确,因为其中的概念正是我所需要的。对于那些寻找完整解决方案的人,请参阅以下代码(和演示!):
function isNotYetStandardProp (propName) {
if (propName.indexOf('not-yet-standard-property-') > -1) {
return true;
}
else {
return false;
}
}
function transformPropName (propName) {
return propName.replace("not-yet-", "");
}
var skipNext = false;
var convertedProps = [];
var newRule;
var newNode;
return function (css) {
css.walkRules(function(rule) {
// The skipNext flag is used to prevent walkRules from walking onto any
// newly created rules and recursively creating new rules based on the
// newly added rule.
if (!skipNext) {
// Reset to remove values from last loop
convertedProps = [];
newRule = null;
newNode = null;
// Search through nodes of current rule and build up array
// containing each property that needs to change.
rule.nodes.filter(function (node) {
if ((node.type === 'decl') && isNotYetStandardProp(node.prop)) {
newNode = node.clone();
newNode.prop = transformPropName(node.prop);
convertedProps.push(newNode);
}
});
// If there were properties to convert in this rule create a new
// rule to put them in.
if (convertedProps.length > 0) {
newRule = rule.cloneAfter({ selector: '.ie ' + rule.selector });
newRule.removeAll().append(convertedProps);
skipNext = true;
}
}
else {
// We only want to skipNext once.
skipNext = false;
}
})
}
Demo of solution
你不需要 walkRules
里面的 walkDecls
,只需使用 rule.nodes
:
css.walkRules(rule => {
const nonStandard = rule.nodes.filter(node => {
return if node.type === 'decl' && checkPropName(node.prop);
});
if ( nonStandard.length > 0 ) {
const clone = rule.cloneAfter({ selector: '.ie ' + rule.selector });
clone.append(nonStandard);
}
})
你好,我一直在尝试 PostCSS,并且在编写一个插件时遇到了一些困难,该插件在发现某些 CSS 属性时会附加一条新规则。
我想要达到的目标...
开始 CSS:
.selector {
color: red;
not-yet-standard-property-a: 10px;
not-yet-standard-property-b: 20px;
}
PostCSS插件后:
.selector {
color: red;
not-yet-standard-property-a: 10px;
not-yet-standard-property-b: 20px;
}
.ie .selector {
standard-property-a: 10px;
standard-property-b: 20px;
}
每当我看到其中一个时,我很容易添加新规则not-yet-standard-property-*
…
return function (css) {
css.walkRules(function (rule) {
rule.walkDecls('not-yet-standard-property-a', function (decl) {
css.insertAfter(rule, '.ie ' + rule.selector + '{ standard-property-a: '+ decl.value +' }');
});
rule.walkDecls('not-yet-standard-property-b', function (decl) {
css.insertAfter(rule, '.ie ' + rule.selector + '{ standard-property-b: '+ decl.value +' }');
});
rule.walkDecls('not-yet-standard-property-c', function (decl) {
css.insertAfter(rule, '.ie ' + rule.selector + '{ standard-property-c: '+ decl.value +' }');
});
});
}
但是输出并不理想……
.selector {
color: red;
not-yet-standard-property-a: 10px;
not-yet-standard-property-b: 20px;
not-yet-standard-property-c: 53px;
}
.ie .selector {
standard-property-c: 53px;
}
.ie .selector {
standard-property-b: 20px;
}
.ie .selector {
standard-property-a: 10px;
}
理想情况下,新规则只会在遍历整个规则后添加一次,但 PostCSS 似乎不允许在 walkRules 函数中使用条件,所以我不确定如何阻止它为它看到的每个规则创建新规则。
我已经链接到上述代码的演示。任何关于更好架构的建议将不胜感激,就像我之前说过的,我对此很陌生。谢谢!
Demo
编辑:已解决。非常感谢 Andrey Sitnik!我已将他的回答标记为正确,因为其中的概念正是我所需要的。对于那些寻找完整解决方案的人,请参阅以下代码(和演示!):
function isNotYetStandardProp (propName) {
if (propName.indexOf('not-yet-standard-property-') > -1) {
return true;
}
else {
return false;
}
}
function transformPropName (propName) {
return propName.replace("not-yet-", "");
}
var skipNext = false;
var convertedProps = [];
var newRule;
var newNode;
return function (css) {
css.walkRules(function(rule) {
// The skipNext flag is used to prevent walkRules from walking onto any
// newly created rules and recursively creating new rules based on the
// newly added rule.
if (!skipNext) {
// Reset to remove values from last loop
convertedProps = [];
newRule = null;
newNode = null;
// Search through nodes of current rule and build up array
// containing each property that needs to change.
rule.nodes.filter(function (node) {
if ((node.type === 'decl') && isNotYetStandardProp(node.prop)) {
newNode = node.clone();
newNode.prop = transformPropName(node.prop);
convertedProps.push(newNode);
}
});
// If there were properties to convert in this rule create a new
// rule to put them in.
if (convertedProps.length > 0) {
newRule = rule.cloneAfter({ selector: '.ie ' + rule.selector });
newRule.removeAll().append(convertedProps);
skipNext = true;
}
}
else {
// We only want to skipNext once.
skipNext = false;
}
})
}
Demo of solution
你不需要 walkRules
里面的 walkDecls
,只需使用 rule.nodes
:
css.walkRules(rule => {
const nonStandard = rule.nodes.filter(node => {
return if node.type === 'decl' && checkPropName(node.prop);
});
if ( nonStandard.length > 0 ) {
const clone = rule.cloneAfter({ selector: '.ie ' + rule.selector });
clone.append(nonStandard);
}
})