修改 Sass 中选择器的中间部分(adding/removing 类 等)
Modifying the middle of a selector in Sass (adding/removing classes, etc.)
我的菜单中有以下 SCSS 样式链接:
nav {
ul {
li {
a {
color: red
}
}
}
ul.opened {
li {
a {
color: green
}
}
}
}
生成以下(正确)CSS:
nav ul li a {
color: red;
}
nav ul.opened li a {
color: green;
}
我尝试修改 JavaScript 以将 class 应用于导航元素,并在 Sass 中使用 selector-append()
附加 class。但这似乎以错误的顺序进行附加(如果参数颠倒,class 附加到最后一个元素!):
nav {
ul {
li {
a {
color: red;
@at-root #{selector-append('.opened', &)} {
color: green;
}
}
}
}
}
输出(不正确!):
nav ul li a {
color: red;
}
.openednav ul li a {
color: green;
}
有没有一种方法可以重写 SCSS 以便可以正确附加 class 而无需重复选择器(类似于 selector-append()
方法)?
简答
由于我们要替换的元素具有唯一的名称,因此我们要查找的是:
nav {
ul {
li {
a {
color: red;
@at-root #{selector-replace(&, 'ul', 'ul.opened')} {
color: green;
}
}
}
}
}
长答案
操纵选择器非常肮脏,除非万不得已,否则我建议不要这样做。如果你通过指定诸如 table tr td
或 ul li
之类的东西来过度限定你的选择器,那么从简化开始:tr 和 ul 在这些选择器中都是多余的(除非你试图避免在有序的元素下设置样式列表)。调整你的嵌套更简单等等
从 Sass 版本 3.4 开始,有 2 个重要功能允许您修改选择器。
- Selector functions
- 父选择器可以存储在变量中
示例:
.foo ul > li a, .bar {
$sel: &;
@debug $sel;
}
你总是会得到一个字符串列表,因为选择器可以用逗号链接在一起,即使你只有一个选择器。
.foo ul > li a, .bar { ... }
(1 2 3 4 5), (1)
您会注意到此处计算后代选择器(Sass 中的列表可以是 space 或逗号分隔)。记住这一点非常重要。
当 selector-replace()
不起作用时
selector-replace()
功能在以下情况下不起作用:
- 您要替换的选择器不是唯一的(例如
ul ul li
)
- 您想插入一个或多个选择器(例如
ul ul li
-> ul ul ul li
)
- 您想删除一个选择器(例如
ul > li
-> ul li
)
在这种情况下,您需要遍历选择器并且需要知道要修改哪个位置。以下函数将采用一个函数,并使用 call() function.
的魔力将其应用于选择器中的特定位置
@function selector-nth($sel, $n, $f, $args...) {
$collector: ();
@each $s in $sel {
$modified: call($f, nth($s, $n), $args...);
$collector: append($collector, set-nth($s, $n, $modified), comma);
}
@return $collector;
}
Append a class (when the selector isn't unique or you don't know its name)
我们在这里需要的函数有 2 个参数:原始选择器和您要附加到它的选择器。使用简单的插值来完成这项工作。
@function append-class($a, $b) {
@return #{$a}#{$b};
}
.foo, .bar {
ul > li a {
color: red;
@at-root #{selector-nth(&, -2, append-class, '.baz')} {
color: blue;
}
}
}
输出:
.foo ul > li a, .bar ul > li a {
color: red;
}
.foo ul > li.baz a, .bar ul > li.baz a {
color: blue;
}
插入选择器
此函数也有 2 个参数:原始选择器和您要在它之前插入的选择器。
@function insert-selector($a, $b) {
@return $b $a;
}
.foo, .bar {
ul > li a {
color: red;
@at-root #{selector-nth(&, -2, insert-selector, '.baz')} {
color: blue;
}
}
}
输出:
.foo ul > li a, .bar ul > li a {
color: red;
}
.foo ul > .baz li a, .bar ul > .baz li a {
color: blue;
}
删除选择器
删除选择器就像用空字符串替换选择器一样简单。
@function remove-selector($sel) {
@return '';
}
.foo, .bar {
ul > li a {
color: red;
@at-root #{selector-nth(&, -2, remove-selector)} {
color: blue;
}
}
}
输出:
.foo ul > li a, .bar ul > li a {
color: red;
}
.foo ul > a, .bar ul > a {
color: blue;
}
TL;DR
选择器只是一个列表。任何列表操作函数都适用于它,您可以遍历它以根据需要对其进行修改。
所以,是的,除非你真的真的需要,否则不要这样做。如果您决定仍然需要它,我已将这些功能打包到 selector-nth library.
我做了一个 mixin 解决了这个问题。
Github: https://github.com/imkremen/sass-parent-append
示例:https://codepen.io/imkremen/pen/RMVBvq
用法(scss):
.ancestor {
display: inline-flex;
.grandparent {
padding: 32px;
background-color: lightgreen;
.parent {
padding: 32px;
background-color: blue;
.elem {
padding: 16px;
background-color: white;
@include parent-append(":focus", 3) {
box-shadow: inset 0 0 0 8px aqua;
}
@include parent-append(":hover") {
background-color: fuchsia;
}
@include parent-append("p", 0, true) {
background-color: green;
}
}
}
}
}
结果(css):
.ancestor {
display: inline-flex;
}
.ancestor .grandparent {
padding: 32px;
background-color: lightgreen;
}
.ancestor .grandparent .parent {
padding: 32px;
background-color: blue;
}
.ancestor .grandparent .parent .elem {
padding: 16px;
background-color: white;
}
.ancestor:focus .grandparent .parent .elem {
box-shadow: inset 0 0 0 8px aqua;
}
.ancestor .grandparent .parent:hover .elem {
background-color: fuchsia;
}
.ancestor .grandparent .parent p.elem {
background-color: green;
}
我的菜单中有以下 SCSS 样式链接:
nav {
ul {
li {
a {
color: red
}
}
}
ul.opened {
li {
a {
color: green
}
}
}
}
生成以下(正确)CSS:
nav ul li a {
color: red;
}
nav ul.opened li a {
color: green;
}
我尝试修改 JavaScript 以将 class 应用于导航元素,并在 Sass 中使用 selector-append()
附加 class。但这似乎以错误的顺序进行附加(如果参数颠倒,class 附加到最后一个元素!):
nav {
ul {
li {
a {
color: red;
@at-root #{selector-append('.opened', &)} {
color: green;
}
}
}
}
}
输出(不正确!):
nav ul li a {
color: red;
}
.openednav ul li a {
color: green;
}
有没有一种方法可以重写 SCSS 以便可以正确附加 class 而无需重复选择器(类似于 selector-append()
方法)?
简答
由于我们要替换的元素具有唯一的名称,因此我们要查找的是:
nav {
ul {
li {
a {
color: red;
@at-root #{selector-replace(&, 'ul', 'ul.opened')} {
color: green;
}
}
}
}
}
长答案
操纵选择器非常肮脏,除非万不得已,否则我建议不要这样做。如果你通过指定诸如 table tr td
或 ul li
之类的东西来过度限定你的选择器,那么从简化开始:tr 和 ul 在这些选择器中都是多余的(除非你试图避免在有序的元素下设置样式列表)。调整你的嵌套更简单等等
从 Sass 版本 3.4 开始,有 2 个重要功能允许您修改选择器。
- Selector functions
- 父选择器可以存储在变量中
示例:
.foo ul > li a, .bar {
$sel: &;
@debug $sel;
}
你总是会得到一个字符串列表,因为选择器可以用逗号链接在一起,即使你只有一个选择器。
.foo ul > li a, .bar { ... }
(1 2 3 4 5), (1)
您会注意到此处计算后代选择器(Sass 中的列表可以是 space 或逗号分隔)。记住这一点非常重要。
当 selector-replace()
不起作用时
selector-replace()
功能在以下情况下不起作用:
- 您要替换的选择器不是唯一的(例如
ul ul li
) - 您想插入一个或多个选择器(例如
ul ul li
->ul ul ul li
) - 您想删除一个选择器(例如
ul > li
->ul li
)
在这种情况下,您需要遍历选择器并且需要知道要修改哪个位置。以下函数将采用一个函数,并使用 call() function.
的魔力将其应用于选择器中的特定位置@function selector-nth($sel, $n, $f, $args...) {
$collector: ();
@each $s in $sel {
$modified: call($f, nth($s, $n), $args...);
$collector: append($collector, set-nth($s, $n, $modified), comma);
}
@return $collector;
}
Append a class (when the selector isn't unique or you don't know its name)
我们在这里需要的函数有 2 个参数:原始选择器和您要附加到它的选择器。使用简单的插值来完成这项工作。
@function append-class($a, $b) {
@return #{$a}#{$b};
}
.foo, .bar {
ul > li a {
color: red;
@at-root #{selector-nth(&, -2, append-class, '.baz')} {
color: blue;
}
}
}
输出:
.foo ul > li a, .bar ul > li a {
color: red;
}
.foo ul > li.baz a, .bar ul > li.baz a {
color: blue;
}
插入选择器
此函数也有 2 个参数:原始选择器和您要在它之前插入的选择器。
@function insert-selector($a, $b) {
@return $b $a;
}
.foo, .bar {
ul > li a {
color: red;
@at-root #{selector-nth(&, -2, insert-selector, '.baz')} {
color: blue;
}
}
}
输出:
.foo ul > li a, .bar ul > li a {
color: red;
}
.foo ul > .baz li a, .bar ul > .baz li a {
color: blue;
}
删除选择器
删除选择器就像用空字符串替换选择器一样简单。
@function remove-selector($sel) {
@return '';
}
.foo, .bar {
ul > li a {
color: red;
@at-root #{selector-nth(&, -2, remove-selector)} {
color: blue;
}
}
}
输出:
.foo ul > li a, .bar ul > li a {
color: red;
}
.foo ul > a, .bar ul > a {
color: blue;
}
TL;DR
选择器只是一个列表。任何列表操作函数都适用于它,您可以遍历它以根据需要对其进行修改。
所以,是的,除非你真的真的需要,否则不要这样做。如果您决定仍然需要它,我已将这些功能打包到 selector-nth library.
我做了一个 mixin 解决了这个问题。
Github: https://github.com/imkremen/sass-parent-append
示例:https://codepen.io/imkremen/pen/RMVBvq
用法(scss):
.ancestor {
display: inline-flex;
.grandparent {
padding: 32px;
background-color: lightgreen;
.parent {
padding: 32px;
background-color: blue;
.elem {
padding: 16px;
background-color: white;
@include parent-append(":focus", 3) {
box-shadow: inset 0 0 0 8px aqua;
}
@include parent-append(":hover") {
background-color: fuchsia;
}
@include parent-append("p", 0, true) {
background-color: green;
}
}
}
}
}
结果(css):
.ancestor {
display: inline-flex;
}
.ancestor .grandparent {
padding: 32px;
background-color: lightgreen;
}
.ancestor .grandparent .parent {
padding: 32px;
background-color: blue;
}
.ancestor .grandparent .parent .elem {
padding: 16px;
background-color: white;
}
.ancestor:focus .grandparent .parent .elem {
box-shadow: inset 0 0 0 8px aqua;
}
.ancestor .grandparent .parent:hover .elem {
background-color: fuchsia;
}
.ancestor .grandparent .parent p.elem {
background-color: green;
}