【问题标题】:Modifying the middle of a selector in Sass (adding/removing classes, etc.)在 Sass 中修改选择器的中间(添加/删除类等)
【发布时间】:2015-04-20 00:52:15
【问题描述】:

我的菜单中有以下用于样式链接的 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 以将类应用到导航元素,并在 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() 方法)?

【问题讨论】:

    标签: sass css-selectors


    【解决方案1】:

    我做了一个 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;
    }
    

    【讨论】:

      【解决方案2】:

      简短的回答

      由于我们要替换的元素有一个唯一的名称,所以我们要查找的是:

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

      长答案

      操作选择器是非常肮脏的,除非你绝对必须这样做,否则我建议不要这样做。如果您通过指定 table tr tdul li 之类的内容来过度限定选择器,那么首先要简化:tr 和 ul 在这些选择器中都是多余的(除非您试图避免在有序列表下设置元素样式)。调整你的嵌套更简单,等等。

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

      例子:

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

      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 中。

      【讨论】:

        猜你喜欢
        • 2015-03-19
        • 2015-06-13
        • 2014-06-07
        • 1970-01-01
        • 2018-03-16
        • 1970-01-01
        相关资源
        最近更新 更多