2. 嵌套CSS 规则;
css
中重复写选择器是非常恼人的。如果要写一大串指向页面中同一块的样式时,往往需要一遍又一遍地写同一个ID
:
#content article h1 { color: #333 }
#content article p { margin-bottom: 1.4em }
#content aside { background-color: #EEE }
像这种情况,sass
可以让你只写一遍,且使样式可读性更高。在Sass中,你可以像俄罗斯套娃那样在规则块中嵌套规则块。sass
在输出css
时会帮你把这些嵌套规则处理好,避免你的重复书写。
#content {
article {
h1 { color: #333 }
p { margin-bottom: 1.4em }
}
aside { background-color: #EEE }
}
/* 编译后 */
#content article h1 { color: #333 }
#content article p { margin-bottom: 1.4em }
#content aside { background-color: #EEE }
上边的例子,会在输出css
时把它转换成跟你之前看到的一样的效果。这个过程中,sass
用了两步,每一步都是像打开俄罗斯套娃那样把里边的嵌套规则块一个个打开。首先,把#content
(父级)这个id
放到article
选择器(子级)和aside
选择器(子级)的前边:
#content {
article {
h1 { color: #333 }
p { margin-bottom: 1.4em }
}
#content aside { background-color: #EEE }
}
/* 编译后 */
#content article h1 { color: #333 }
#content article p { margin-bottom: 1.4em }
#content aside { background-color: #EEE }
然后,#content article
里边还有嵌套的规则,sass
重复一遍上边的步骤,把新的选择器添加到内嵌的选择器前边。
一个给定的规则块,既可以像普通的CSS那样包含属性,又可以嵌套其他规则块。当你同时要为一个容器元素及其子元素编写特定样式时,这种能力就非常有用了。
#content {
background-color: #f5f5f5;
aside { background-color: #eee }
}
容器元素的样式规则会被单独抽离出来,而嵌套元素的样式规则会像容器元素没有包含任何属性时那样被抽离出来。
#content { background-color: #f5f5f5 }
#content aside { background-color: #eee }
大多数情况下这种简单的嵌套都没问题,但是有些场景下不行,比如你想要在嵌套的选择器里边立刻应用一个类似于:hover
的伪类。为了解决这种以及其他情况,sass
提供了一个特殊结构&
。
2-1. 父选择器的标识符&;
一般情况下,sass
在解开一个嵌套规则时就会把父选择器(#content
)通过一个空格连接到子选择器的前边(article
和aside
)形成(#content article
和#content aside
)。这种在CSS里边被称为后代选择器,因为它选择ID为content
的元素内所有命中选择器article
和aside
的元素。但在有些情况下你却不会希望sass
使用这种后代选择器的方式生成这种连接。
最常见的一种情况是当你为链接之类的元素写:hover
这种伪类时,你并不希望以后代选择器的方式连接。比如说,下面这种情况sass
就无法正常工作:
article a {
color: blue;
:hover { color: red }
}
这意味着color: red
这条规则将会被应用到选择器article a :hover
,article
元素内链接的所有子元素在被hover
时都会变成红色。这是不正确的!你想把这条规则应用到超链接自身,而后代选择器的方式无法帮你实现。
解决之道为使用一个特殊的sass
选择器,即父选择器。在使用嵌套规则时,父选择器能对于嵌套规则如何解开提供更好的控制。它就是一个简单的&
符号,且可以放在任何一个选择器可出现的地方,比如h1
放在哪,它就可以放在哪。
article a {
color: blue;
&:hover { color: red }
}
当包含父选择器标识符的嵌套规则被打开时,它不会像后代选择器那样进行拼接,而是&
被父选择器直接替换:
article a { color: blue }
article a:hover { color: red }
在为父级选择器添加:hover
等伪类时,这种方式非常有用。同时父选择器标识符还有另外一种用法,你可以在父选择器之前添加选择器。举例来说,当用户在使用IE浏览器时,你会通过JavaScript
在<body>
标签上添加一个ie的类名,为这种情况编写特殊的样式如下:
#content aside {
color: red;
body.ie & { color: green }
}
/*编译后*/
#content aside {color: red};
body.ie #content aside { color: green }
sass
在选择器嵌套上是非常智能的,即使是带有父选择器的情况。当sass
遇到群组选择器(由多个逗号分隔开的选择器形成)也能完美地处理这种嵌套。
2-2. 群组选择器的嵌套;
在CSS
里边,选择器h1
h2
和h3
会同时命中h1元素、h2元素和h3元素。与此类似,.button
button
会命中button元素和类名为.button的元素。这种选择器称为群组选择器。群组选择器的规则会对命中群组中任何一个选择器的元素生效。
.button, button {
margin: 0;
}
当看到上边这段代码时,你可能还没意识到会有重复性的工作。但会很快发现:如果你需要在一个特定的容器元素内对这样一个群组选择器进行修饰,情况就不同了。css
的写法会让你在群组选择器中的每一个选择器前都重复一遍容器元素的选择器。
.container h1, .container h2, .container h3 { margin-bottom: .8em }
非常幸运,sass
的嵌套特性在这种场景下也非常有用。当sass
解开一个群组选择器规则内嵌的规则时,它会把每一个内嵌选择器的规则都正确地解出来:
.container {
h1, h2, h3 {margin-bottom: .8em}
}
首先sass
将.container
和h1
.container
和h2
.container
和h3
分别组合,然后将三者重新组合成一个群组选择器,生成你前边看到的普通css
样式。对于内嵌在群组选择器内的嵌套规则,处理方式也一样:
nav, aside {
a {color: blue}
}
首先sass
将nav
和a
aside
和a
分别组合,然后将二者重新组合成一个群组选择器:
nav a, aside a {color: blue}
处理这种群组选择器规则嵌套上的强大能力,正是sass
在减少重复敲写方面的贡献之一。尤其在当嵌套级别达到两层甚至三层以上时,与普通的css
编写方式相比,只写一遍群组选择器大大减少了工作量。
有利必有弊,你需要特别注意群组选择器的规则嵌套生成的css
。虽然sass
让你的样式表看上去很小,但实际生成的css
却可能非常大,这会降低网站的速度。
关于选择器嵌套的最后一个方面,我们看看sass
如何处理组合选择器,比如>、+和~的使用。你将看到,这种场景下你甚至无需使用父选择器标识符。
2-3. 子组合选择器和同层组合选择器:>、+和~;
上边这三个组合选择器必须和其他选择器配合使用,以指定浏览器仅选择某种特定上下文中的元素。
article section { margin: 5px }
article > section { border: 1px solid #ccc }
你可以用子组合选择器>选择一个元素的直接子元素。上例中,第一个选择器会选择article下的所有命中section选择器的元素。第二个选择器只会选择article下紧跟着的子元素中命中section选择器的元素。
在下例中,你可以用同层相邻组合选择器+
选择header
元素后紧跟的p
元素:
header + p { font-size: 1.1em }
你也可以用同层全体组合选择器~
,选择所有跟在article
后的同层article
元素,不管它们之间隔了多少其他元素:
article ~ article { border-top: 1px dashed #ccc }
这些组合选择器可以毫不费力地应用到sass
的规则嵌套中。可以把它们放在外层选择器后边,或里层选择器前边:
article {
~ article { border-top: 1px dashed #ccc }
> section { background: #eee }
dl > {
dt { color: #333 }
dd { color: #555 }
}
nav + & { margin-top: 0 }
}
sass
会如你所愿地将这些嵌套规则一一解开组合在一起:
article ~ article { border-top: 1px dashed #ccc }
article > footer { background: #eee }
article dl > dt { color: #333 }
article dl > dd { color: #555 }
nav + article { margin-top: 0 }
在sass
中,不仅仅css
规则可以嵌套,对属性进行嵌套也可以减少很多重复性的工作。
2-4. 嵌套属性;
在sass
中,除了CSS选择器,属性也可以进行嵌套。尽管编写属性涉及的重复不像编写选择器那么糟糕,但是要反复写border-style
border-width
border-color
以及border-*
等也是非常烦人的。在sass
中,你只需敲写一遍border
:
nav {
border: {
style: solid;
width: 1px;
color: #ccc;
}
}
嵌套属性的规则是这样的:把属性名从中划线-的地方断开,在根属性后边添加一个冒号:,紧跟一个{ }
块,把子属性部分写在这个{ }
块中。就像css
选择器嵌套一样,sass
会把你的子属性一一解开,把根属性和子属性部分通过中划线-连接起来,最后生成的效果与你手动一遍遍写的css
样式一样:
nav {
border-style: solid;
border-width: 1px;
border-color: #ccc;
}
对于属性的缩写形式,你甚至可以像下边这样来嵌套,指明例外规则:
nav {
border: 1px solid #ccc {
left: 0px;
right: 0px;
}
}
这比下边这种同等样式的写法要好:
nav {
border: 1px solid #ccc;
border-left: 0px;
border-right: 0px;
}
属性和选择器嵌套是非常伟大的特性,因为它们不仅大大减少了你的编写量,而且通过视觉上的缩进使你编写的样式结构更加清晰,更易于阅读和开发。
即便如此,随着你的样式表变得越来越大,这种写法也很难保持结构清晰。有时,处理这种大量样式的唯一方法就是把它们分拆到多个文件中。sass
通过对css
原有@import
规则的改进直接支持了这一特性。