CSS 选择器种类繁多,其中几个长得还比较像,如果有的属性用的不多,一会就忘记了,特别是CSS3中的:nth选择器,那么从现在开始从头一起来看看CSS选择器的运用。

CSS是一种用于屏幕上渲染html,xml等一种语言,CSS主要是在相应的元素中应用样式,来渲染相对应用的元素,那么这样我们选择相应的元素就很重要了,如何选择对应的元素,此时就需要我们所说的选择器。选择器主要是用来确定html的树形结构中的DOM元素节点。我把CSS选择器分开成三部分,第一部分是我们常用的部分,我把他叫做基本选择器;第二部分我把他称作是属性选择器,第三部分我把他称作伪类选择器,这一部分也是最难理解和掌握的部分,先来看第一部分——基本选择器。

为了更好的说明问题,先创建一个简单的DOM结构,如下:

加些CSS,效果如下所示:

基本选择器

通配符选择器(*)

我们平常不加reset.css的时候 都会加上

1
2
3
4
* {
margin: 0;
padding: 0;
}

所有浏览器支持通配符选择器。

元素选择器(E)

元素选择器,是css选择器中最常见而且最基本的选择器。元素选择器其实就是文档的元素,如html,body,p,div等等,比如我们这个demo:中元素包括了div,ul,li等。

所有浏览器支持元素选择器(E)。

类选择器(.className)

类选择器是以一独立于文档元素的方式来指定样式,使用类选择器之前需要在html元素上定义类名,换句话说需要保证类名在html标记中存在,这样才能选择类.

类选择器还可以结合元素选择器来使用,比如说,你文档中有好多个元素使用了类名“items”,但你只想在p元素这个类名上修改样式,那么你可以这样进行选择并加上相应的样式:

上面代码只会对p元素并且是其有一个类名叫“items”。不符合这两个条件的都不会被选择。

所有浏览器都支持类选择器,但多类选择器(.className1.className2)不被ie6支持。

id选择器(#ID)

ID选择器和上面说的类选择器是很相似的,在使用ID选择器之前也需要先在html文档中加注ID名称,这样在样式选择器中才能找到相对应的元素,不同的是ID选择器是一个页面中唯一的值,我们在类使用时是在相对应的类名前加上一个“.”号(.className)而id选择器是在名称前使用”#”如(#id)

所有浏览器都支持ID选择器。

那么什么时候采用id命名?什么时候采用类命名呢?我个人认为就是关键的一点就是具有唯一性使用id选择器;公用的,类似的使用类选择器。使用这两个选择器时,最好区别大小写。

后代选择器(E F)

后代选择器也被称作包含选择器,所起作用就是可以选择某元素的后代元素,比如说:E F,前面E为祖先元素,F为后代元素,所表达的意思就是选择了E元素的所有后代F元素,请注意他们之间需要一个空格隔开。这里F不管是E元素的子元素或者是孙元素或者是更深层次的关系,都将被选中,换句话说,不论F在E中有多少层关系,都将被选中

所有浏览器都支的后代选择器。

子元素选择器(E>F)

子元素选择器只能选择某元素的子元素,其中E为父元素,而F为子元素,其中E>F所表示的是选择了E元素下的所有子元素F。这和后代选择器(E F)不一样,在后代选择器中F是E的后代元素,而子元素选择器E > F,其中F仅仅是E的子元素而已.

IE6不支持子元素选择器。

相邻兄弟元素选择器(E + F)

相邻兄弟选择器可以选择紧接在另一元素后的元素,而且他们具有一个相同的父元素,换句话说,EF两元素具有一个相同的父元素,而且F元素在E元素后面,而且相邻,这样我们就可以使用相邻兄弟元素选择器来选择F元素。

上面代码表示选择li的相邻元素li,我们这里一共有十个li,那么上面的代码选择了从第2个li到 10 个li,一共九个,请看效果:

因为上面的li+li其中第二li是第一li的相邻元素,第三个又是第二个相邻元素,因此第三个也被选择,依此类推,所以后面九个li都被选中了,如果我们换过一种方式来看,可能会更好的理解一点:

按照前面所讲的知识,这句代码很明显选择了li.active后面相邻的li元素,注意了和li.active后面相邻的元素仅只有一个的。如图:

IE6不支持这个选择器

通用兄弟选择器(E ~ F)

通用兄弟元素选择器是CSS3新增加一种选择器,这种选择器将选择某元素后面的所有兄弟元素,他们也和相邻兄弟元素类似,需要在同一个父元素之中,换句话说,E和F元素是属于同一父元素之内,并且F元素在E元素之后,那么E ~ F 选择器将选择中所有E元素后面的F元素。比如下面的代码:

上面的代码所表示的是,选择中了li.active 元素后面的所有兄弟元素li,如图所示:

通用兄弟选择器和相邻兄弟选择器极其相似,只不过,相邻兄弟选择器仅选择是元素的仅与其相邻的后面元素(选中的仅一个元素);而通用兄弟元素选择器,选中的是元素相邻的后面兄弟元素,这样说起来可能会有迷糊,大家可以仔细看看其相邻兄弟的效果图。

IE6不支持这种选择器的用法。

群组选择器(selector1,selector2,…,selectorN)

群组选择器是将具有相同样式的元素分组在一起,每个选择器之间使用逗号“,”隔开,如上面所示selector1,selector2,…,selectorN。这个逗号告诉浏览器,规则中包含多个不同的选择器,如果不有这个逗号,那么所表达的意就完全不同了,省去逗号就成了我们前面所说的后代选择器,这一点大家在使用中千万要小心加小心。我们来看一个简单的例子:

因为li.first和li.last具有相同的样式效果,所以我们把他们写到一个组里来:

所有浏览器都支持群组选择器。

属性选择器

属性选择器早在CSS2中就被引入了,其主要作用就是对带有指定属性的HTML 元素设置样式。使用CSS3属性选择器,你可以只指定元素的某个属性,或者你还可以同时指定元素的某个属性和其对应的属性值。CSS3的属性选择器主要包括以下几种:

E[attr]:只使用属性名,但没有确定任何属性值;
E[attr="value"]:指定属性名,并指定了该属性的属性值;
E[attr~="value"]:指定属性名,并且具有属性值,此属性值是一个词列表,并且以空格隔开,其中词列表中包含了一个value词,而且等号前面的“〜”不能不写;
E[attr^="value"]:指定了属性名,并且有属性值,属性值是以value开头的;
E[attr$="value"]:指定了属性名,并且有属性值,而且属性值是以value结束的;
E[attr*="value"]:指定了属性名,并且有属性值,而且属值中包含了value;
E[attr|="value"]:指定了属性名,并且属性值是value或者以“value-”开头的值(比如说zh-cn);

为了更好的说明CSS3属性选择器的使用方法,我们把第一节的demo换成别的结构,如下所示:

{% image http://opolpcrco.bkt.clouddn.com/17-5-18/30487575-file_1495114314759_12082.png '' '' %}

再加一点CSS美化后,下面就开始针对上面列出的每个属性选择器来,具体分析其使用方法。

E[attr]

E[attr]属性选择器是CSS3属性选择器中最简单的一种。如果你希望选择有某个属性的元素,而不论这个属性值是什么,你就可以使用这个属性选择器,如:

{% image http://opolpcrco.bkt.clouddn.com/17-5-18/18967267-file_1495114376981_4fe0.png '' '' %}

上面代码所表示的,选择了div.demo下所有带有id属性的a元素,并在这个元素上使用背景色为兰色,前景色为黄色,字体加粗的样式,对照上面的html,我们不难发现,只有第一个和最后一个链接使用了id属性,所以选中了这两个a元素,效果如下所示:

{% image http://opolpcrco.bkt.clouddn.com/17-5-18/86927423-file_1495114576494_774e.png '' '' %}

IE6不支持这个选择器。

E[attr=”value”]

E[attr=”value”]选择器和E[attr]选择器,从字面上就能很清楚的理解出来,E[attr=”value”]是指定了属性值“value”,而E[attr]只是选择了有对应的属性,并没有明确指其对应的属性值”value”,这也是这两种选择器的最大区是之处。从而缩小了选择 围,更能精确选择自己需要的元素,在前面实例基础上我们进行一下简单的修改:

{% image http://opolpcrco.bkt.clouddn.com/17-5-18/45776259-file_1495114609686_e6f6.png '' '' %}

和前面代码相比较,此处在id的属性基础上指定了相应的value值为“first”,这样一来我们选中的是div.demo中的a元素,并且这个元素有一个”id=”first””属性值,请看下在的效果图:

{% image http://opolpcrco.bkt.clouddn.com/17-5-18/13169749-file_1495114725681_16d42.png '' '' %}

E[attr=”value”]属性选择器也可以多个属性并写,进一步缩小选择范围

对于E[attr=”value”]这种属性值选择器有一点需要注意:属性和属性值必须完全匹配,特别是对于属性值是词列表的形式时,如:

{% image http://opolpcrco.bkt.clouddn.com/17-5-18/74026612-file_1495114793214_1bcf.png '' '' %}

例如上面的代码,如果你写成:

{% image http://opolpcrco.bkt.clouddn.com/17-5-18/6491914-file_1495114847841_18530.png '' '' %}

面的属性选择器并不会和上在的html匹配,因为他们的属性和属性值没有完全匹配,需要改成如下所示的代码,才能正确匹配:

{% image http://opolpcrco.bkt.clouddn.com/17-5-18/17402346-file_1495114854197_11891.png '' '' %}

IE6浏览器不支持这种选择器。

E[attr~=”value”]

↑ 的例子中, .demo a[class=’links’] 识别不了class='links item' 这样的词列表,那如何才能写一个词,却能识别整个列表呢?这时,E[attr~=”value”]就派上用场了

上面代码表示的是,div.demo下的a元素的title属性中,只要其属性值中含有”website”这个词就会被选择,回头看看我们的html,不难发现所有a元素中“2,6,7,8”这四个a元素的title中都含有,所以被选中,请看效果:

IE6不支持E[attr~=”value”]属性选择器。

E[attr^=”value”]

E[attr^=”value”]属性选择器,指的是选择attr属性值以“value”开头的所有元素,换句话说,选择的属性其以对应的属性值是以“value”开始的,一起来看个实例:

上面代码表示的是选择了以href属性,并且以”http://"和"mailto:"开头的属性值的所有a元素,换过更简单一点的呢?只要a元素中的href属性值是以"http://"或"mailto:"开头的a元素都会以选中,那么下面大家请对照上面的html和下面的效果图,看看是不是那么一回事:

IE6不支持E[attr^=”value”]选择器。

E[attr$=”value”]

E[attr$=”value”]属性选择器刚好与E[attr^=”value”]选择器相反,E[attr$=”value”]表示的是选择attr属性值以”value”结尾的所有元素,换句话说就是选择元素attr属性,并且他的属性值是以value结尾的,这个运用在给你一些特殊的链接加背景图片很方便的,比如说给pdf,png,doc等不同文件加上不同icon,我们就可以使用这个属性来实现,如:

上面代码表示的是,选择div.demo中元素有href属性,并以png值结尾的a元素。(正如上面所说,只不过这里使用的是改变元素的背景色),效果如下:

IE6不支持E[attr$=”value”]属性选择器。

E[attr*=”value”]

E[attr*=”value”]属性选择器表示的是选择attr属性值中包含子串”value”的所有元素。也就是说,只要你所选择的属性,其属性值中有这个”value”值都将被选中,如:

上面代码表示的是:选择了div.demo中a元素,而a元素的title属性中只要有”site”就符合选择条件。效果如下:

E[attr|=”value”]

E[attr|=”value”]是属性选择器中的最后一种,在说这个选择器使用之前先提醒大家attr后面的是一个竖线“|”而不是l,小心搞错了。E[attr|=”value”]被称作为特定属性选择器。这个选择器会选择attr属性值等于value或以value-开头的所有元素,我们来看个实例:

上面的代码会选中了div.demo中lang属性等于zh或以zh-开头的所有a元素,大家可以对照前面的html代友,其中”2,3,4,6”被选中,因为他们都有一个lang属性,并且他们的属性值都符合以”zh”或”zh-“开始的元素。具体效果如下:

所以这种属性选择器用来匹配以“女value-1”,”value-2”的属性是很方便的,比如说你页面中有很多图片,图片文件名都是以”figure-1”,”figure-2”这样的方式来命名的,那么使用这种选择器选中图片就很方便了,大家可以在本地尝试一下,这种属性选择器最常常用的地方是如上面的示例用来匹配语言。

IE6不支持E[attr|=”value”]选择器。

CSS预处理器作用

前言

如今网站的复杂度已经不可同日而语,原生 CSS 已经让开发者力不从心,预处理应运而生,以stylus为例,下面列举几个比较重要的功能:

文件切分

页面越来越复杂,需要加载的 CSS 文件也越来越大,我们有必要把大文件切分开来,否则难以维护。传统的 CSS 文件切分方案基本上就是 CSS 原生的 @import 指令,或在 HTML 中加载多个 CSS 文件,这些方案通常不能满足性能要求。

CSS 预处理器扩展了 @import 指令的能力,通过编译环节将切分后的文件重新合并为一个大文件。这一方面解决了大文件不便维护的问题,另一方面也解决了一堆小文件在加载时的性能问题。

模块化

把文件切分的思路再向前推进一步,就是 “模块化”。一个大的 CSS 文件在合理切分之后,所产生的这些小文件的相互关系应该是一个树形结构。

树形的根结节一般称作 “入口文件”,树形的其它节点一般称作 “模块文件”。入口文件通常会依赖多个模块文件,各个模块文件也可能会依赖其它更末端的模块,从而构成整个树形。

以下是一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
entry.styl
├─ base.styl
│ ├─ normalize.styl
│ └─ reset.styl
├─ layout.styl
│ ├─ header.styl
│ │ └─ nav.styl
│ └─ footer.styl
├─ section-foo.styl
├─ section-bar.styl
└─ ...

如果你用过其它拥有模块机制的编程语言,应该已经深有体会,模块化是一种非常好的代码组织方式,是开发者设计代码结构的重要手段。模块可以很清晰地实现代码的分层、复用和依赖管理,让 CSS 的开发过程也能享受到现代程序开发的便利。

选择符嵌套

选择符嵌套是文件内部的代码组织方式,它可以让一系列相关的规则呈现出层级关系。在以前,如果要达到这个目的,我们只能这样写:

1
2
3
.nav {margin: auto /* 水平居中 */; width: 1000px; color: #333;}
.nav li {float: left /* 水平排列 */; width: 100px;}
.nav li a {display: block; text-decoration: none;}

这种写法需要我们手工维护缩进关系,当上级选择符发生变化时,所有相关的下级选择符都要修改;此外,把每条规则写成一行也不易阅读,为单条声明写注释也很尴尬(只能插在声明之间了)。

在 CSS 预处理语言中,嵌套语法可以很容易地表达出规则之间的层级关系,为单条声明写注释也很清晰易读:

1
2
3
4
5
6
7
8
9
10
.nav
margin: auto // 水平居中
width: 1000px
color: #333
li
float: left // 水平排列
width: 100px
a
display: block
text-decoration: none

变量

在变更出现之前,CSS 中的所有属性值都是 “幻数”。你不知道这个值是怎么来的、它的什么样的意义。有了变量之后,我们就可以给这些 “幻数” 起个名字了,便于记忆、阅读和理解。

接下来我们会发现,当某个特定的值在多处用到时,变量就是一种简单而有效的抽象方式,可以把这种重复消灭掉,让你的代码更加 DRY。

我们来比较一下以下两段代码:

1
2
3
4
5
6
7
8
9
10
11
/* 原生 CSS 代码 */
strong {
color: #ff4466;
font-weight: bold;
}
/* ... */
.notice {
color: #ff4466;
}
1
2
3
4
5
6
7
8
9
10
11
// 用 Stylus 来写
$color-primary = #ff4466
strong
color: $color-primary
font-weight: bold
/* ... */
.notice
color: $color-primary

你可能已经意识到了,变量让开发者更容易实现网站视觉风格的统一,也让 “换肤” 这样的需求变得更加轻松易行。

运算

光有变量还是不够的,我们还需要有运算。如果说变量让值有了意义,那么运算则可以让值和值建立关联。有些属性的值其实跟其它属性的值是紧密相关的,CSS 语法无法表达这层关系;而在预处理语言中,我们可以用变量和表达式来呈现这种关系。

举个例子,我们需要让一个容器最多只显示三行文字,在以前我们通常是这样写的:

1
2
3
4
5
.wrapper {
overflow-y: hidden;
line-height: 1.5;
max-height: 4.5em; /* = 1.5 x 3 */
}

可以发现,我们只能用注释来表达 max-height 的值是怎么来的,而且注释中 3 这样的值也是幻数,还需要进一步解释。未来当行高或行数发生变化的时候, max-height 的值和注释中的算式也需要同步更新,维护起来很不方便。

接下来我们用预处理语言来改良一下:

1
2
3
4
5
6
7
.wrapper
$max-lines = 3
$line-height = 1.5
overflow-y: hidden
line-height: $line-height
max-height: unit($line-height * $max-lines, 'em')

乍一看,代码行数似乎变多了,但代码的意图却更加清楚了——不需要任何注释就把整件事情说清楚了。在后期维护时,只要修改那两个变量就可以了。

值得一提的是,这种写法还带来另一个好处。 $line-height 这个变量可以是 .wrapper 自己定义的局部变量(比如上面那段代码),也可以从更上层的作用域获取:

1
2
3
4
5
6
7
8
9
10
$line-height = 1.5 // 全局统一行高
body
line-height: $line-height
.wrapper
$max-lines = 3
max-height: unit($line-height * $max-lines, 'em')
overflow-y: hidden

这意味着 .wrapper 可以向祖先继承行高,而不需要为这个 “只显示三行” 的需求把自己的行高写死。有了运算,我们就有能力表达属性与属性之间的关联,它令我们的代码更加灵活

函数

把常用的运算操作抽象出来,我们就得到了函数。

开发者可以自定义函数,预处理器自己也内置了大量的函数。最常用的内置函数应该就是颜色的运算函数了吧!有了它们,我们甚至都不需要打开 Photoshop 来调色,就可以得到某个颜色的同色系变种了。

举个例子,我们要给一个按钮添加鼠标悬停效果,而最简单的悬停效果就是让按钮的颜色加深一些。我们写出的 CSS 代码可能是这样的:

1
2
3
4
5
6
.button {
background-color: #ff4466;
}
.button:hover {
background-color: #f57900;
}

我相信即使是最资深的视觉设计师,也很难分清 #ff4466 和 #f57900 这两种颜色到底有什么关联。而如果我们的代码是用预处理语言来写的,那事情就直观多了:

1
2
3
4
5
6
button
$color = #ff9833
background-color: $color
&:hover
background-color: darken($color, 20%)

此外,预处理器的函数往往还支持默认参数、具名实参、 arguments 对象等高级功能,内部还可以设置条件分支,可以满足复杂的逻辑需求。