codecamp

选择器嵌套

Sass 中一个正在被众多开发者滥用的功能,就是选择器嵌套。选择器嵌套为样式表作者提供了一个通过局部选择器的相互嵌套实现全局选择的方法。

一般规则

比如下述Sass选择器的嵌套:

.foo {
  .bar {
    &:hover {
      color: red;
    }
  }
}

生成的 CSS:

.foo .bar:hover {
  color: red;
}

从 Sass3.3 开始,可以在同一行中使用最近选择器引用(&)来实现高级选择器,比如:

.foo {
  &-bar {
    color: red;
  }
}

生成的 CSS:

.foo-bar {
  color: red;
}

这种方式通常被用来配合 BEM 全名方式使用,基于原选择器(比如 .block)生成 .block__element and .block--modifier 选择器。

传说中,使用 & 能在当前选择器下产生新的选择器,这样代码库中选择器无法控制,因为他们本身不存在

选择器嵌套最大的问题是将使最终的代码难以阅读。开发者需要花费巨大精力计算不同缩进级别下选择器具体的表现效果。CSS 最终的表现效果往往不是浅显易懂的。

选择器越具体则声明语句越冗长,而且对最近选择器的引用(&)也越频繁。在某些时候,出现混淆选择器路径和探索下一级选择器的错误率很高,这非常不值得。

为了防止此类情况,我们应该尽可能避免选择器嵌套。然而,显然只有少数情况适应这一措施。

例外

首先,在最外层选择器中嵌套伪类和伪元素是被允许,也是受推荐的。

.foo {
  color: red;

  &:hover {
    color: green;
  }

  &::before {
    content: 'pseudo-element';
  }
}

使用选择器嵌套选择伪类和伪元素不仅仅有道理的(因为它的处理功能与选择器紧密相关),而且有助于保持总体的一致性。

当然,如果使用类似 .is-active 类名来控制当前选择器状态,也可以这样使用选择器嵌套。

.foo {
  // ...

  &.is-active {
    font-weight: bold;
  }
}

这并不是最重要的,当一个元素的样式在另一个容器中有其他指定的样式时,可以使用嵌套选择器让他们保持在同一个地方。

.foo {
  // ...

  .no-opacity & {
    display: none;
  }
}

当一个没太多经验的开发者,不对类似于 .no-opacity & 这样的选择器造成混淆。可能创建一个 mixin 来处理。

/// Helper mixin to provide simple API to selector nesting
/// @param {String} $selector - Selector
@mixin when-inside($selector) {
  #{$selector} & {
    @content;
  }
}

重写刚才的示例,他看起来像这样:

.foo {
  // ...

  @include when-inside('.no-opacity') {
    display: none;
  }
}

这所有的一切,有些是无关紧要的细节,关键是要保持一致性。如果你觉得完全有信心搞定选择器嵌套,然后你就使用了选择器嵌套。可你还要确保你的整个团队也能搞定选择器的嵌套。

扩展阅读

如果你喜欢 Sass Guidelines,请支持它

 支持 Sass Guidelines

声明顺序
常量
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

命名约定

循环

For

警告和错误

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }