在Sass中修改选择器的中间(添加/删除类等)

时间:2015-02-18 20:40:22

标签: sass css-selectors

我的菜单中有样式链接的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以将类应用于nav元素,并在Sass中使用selector-append()来附加类。但这似乎以错误的顺序进行追加(如果参数被反转,则该类将附加到最后一个元素!):

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,以便可以正确附加类而无需复制选择器(类似于selector-append()方法)?

2 个答案:

答案 0 :(得分:11)

简答

由于我们要替换的元素具有唯一的名称,我们正在寻找的是:

nav {
    ul {
        li {
            a {
                color: red;

                @at-root #{selector-replace(&, 'ul', 'ul.opened')} {
                    color: green;
                }
            }
        }
    }
}

答案很长

操纵选择器非常脏,除非你绝对必须,否则我会反对它。如果你通过指定诸如table tr tdul li之类的东西来对你的选择器进行过度限定,那么首先要简化:tr和ul在这些选择器中都是多余的(除非你试图避免在有序的下面使用样式元素)列表)。调整您的嵌套更简单等。

从Sass 3.4版开始,有两个重要的功能允许您修改选择器。

示例:

.foo ul > li a, .bar {
    $sel: &;
    @debug $sel;
}

您将始终获得字符串列表的列表,因为选择器可以用逗号链接在一起,即使您只有一个选择器。

.foo ul > li a, .bar { ... }
 (1  2  3  4 5), (1)

你会注意到后代选择器在这里被计数(Sass中的列表可以是空格或逗号分隔)。记住这一点非常重要。


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;
}

附加一个类(当选择器不是唯一的或你不知道它的名字时)

我们需要的函数有两个参数:原始选择器和你想要附加的选择器。使用简单插值来完成工作。

@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;
}

插入选择器

这个函数还有两个参数:原始选择器和你想在它之前插入的选择器。

@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

答案 1 :(得分:0)

我做了一个解决这个问题的混音。

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;
}