CSS 选择器基础

约定:本文使用 jade 模板语言来表示简化的html结构
没接触过 jade 的同学,请看下面的小科普:

1
2
3
4
5
<div class="father">
<span id="child">hello jade!</span>
<img src="jade.jpg"/>
</div>
<! -- a comment -->

上面的html结构可用 jade 表示如下:

1
2
3
4
div.father
span#child hello jade!
img(src="jade.jpg")
// a comment

即:

  1. class用.表示
  2. id用#表示
  3. 属性值写在小括号里
  4. 用两个空格缩进来表示元素之间的层级关系
  5. // 表示注释

选择器种类

1.元素选择器

直接写元素名即可

1
2
3
4
/* css code */
html {
font-size: 100px;
}

2.类选择器

以.开头

1
2
// jade code
div.white.green-bg
1
2
3
4
5
6
7
8
/* css code */
.white {
color: white;
}

.green-bg {
background: green;
}

因为示例div带有两个类,因此还可以使用多个类选择,注意多个类之间不能有空格,不然就变成后代选择器

1
2
3
4
/* css code */
.white.green-bg {
border: 1px outset black;
}

3. id选择器

以#开头

1
2
// jade code
span#child

1
2
3
4
/* css code */
#child {
display: inline-block;
}

以上三个元素器其实是可以组合起来用的,如:

1
2
3
4
p.class, 
p#id {
/* some style */
}

这样的写法并不出错,但若非必要,并不推荐,理由是:

1. 浏览器根据css代码查找元素是从右到左查找的,也即先找到类为.class的元素,再看它是否为p元素。因此上述写法会增加浏览器的查找负担。

2.类/id选择器的速度都比元素选择器要快,所以如果可以使用类/id选择器来选择元素的话,何必再加多个元素选择,画蛇添足呢

3.选择不要“多得过分”。`html  body header nav li a ` 像这样的选择器,看起来好像十分精确,但明显html与body是多余的,这样会降低浏览器查找效率的。一般而言,三四个类/元素的组合最佳。

4.实战表明,id选择器若非"情非得已"还是少用,多用类选择器与元素选择器(具体情况具体分析,记住id必须唯一)

4.属性选择器

使用[] 括号里面填写属性值,常用的有两种:

1
2
3
4
5
6
7
//jade code

img(alt="null src")
img(src="../images/demo.jpg" alt="have src")

input(placeholder="不指定type,默认为text")
input(type="email")
1
2
3
4
5
6
7
8
/* css code */
img[src] {
/* 选择src属性不为空的img元素,即第2个img元素 */
}

inpnt[type="text"] {
/* 选择type为text的input元素,上面两个input都选不到 */
}

注意:
1.没有属性不等于某值的选择器,即 input[type!=”text”] 是行不通的

2.元素不指定属性则属性选择器选不到,故想选择
input[type="text"] 时一定要显示指定input的type

3.input[type="text phone"] 这种写法是行不通的,只能老老实实地写
input[type="text"], input[type="phone"] { }

5.根据文档结构选择

考虑下列文档结构

//jade code
body
  header
    ul
      li
        a(href="index.html") first link
      li
        a(href="about.html") second link 
  a#body-link(href="body.html") link in body     
  div#before-footer 
  footer
    a(href="other.html") link in footer

5.1 选择后代

选择器之间带空格

1
2
3
4
5
6
body a {
/*
选择 body 元素下的所有 a 元素,不管嵌套有多深
注意到,这里是有空格的,同理,如果类名之前也带空格,那也是选择后代
*/
}

5.2 选择孩子

使用 >

1
2
3
4
5
6
body > a {
/*
选择 body 元素的直接子代 a 元素
只有 #body-link 被选中
*/
}

5.3 选择相邻的兄弟

使用 + 选择下一个元素

1
2
3
4
5
6
#before-footer + footer {
/*
选择 #before-footer 的下一个 footer 元素,
也即示例结构中唯一的footer元素被选中
*/
}

5.4 选择后面所有的兄弟

使用 ~
如果元素之间间隔太远,使用 + 选择不到,可以使用 ~

1
2
3
4
5
6
#body-link ~ footer {
/*
选择 #body-link 后面所有的 footer 兄弟元素,
也即示例结构中唯一的footer元素被选中
*/
}

注意:
没有选择前面的兄弟的选择器

6.特殊选择器

特殊选择器分为伪类与伪元素。用于给文档元素添加特殊效果,增强表现力。
规范而言:
一个冒号开头的是伪类
两个冒号开头的是伪元素

6.1 伪类

伪类一般与行为/状态有关。可以认为是CSS选择器内置的类,与开发人员自定义的类区分开来(个人观点,帮助理解)

常用伪类

1
2
3
4
5
6
:hover /*鼠标悬停时*/
:active 鼠标按下去时(松开即没有)
:focus 获得焦点时
:first-child 某某元素作为其父元素的第一个子元素
:last-child 某某元素作为其父元素的最后一个子元素
:nth-child(n) 某某元素作为其父元素的第n个子元素

把:first-child讲清楚,这个很容易被误解
考虑以下dom结构

1
2
3
4
5
//jade code
article
p#p1
p#p2
p#p3

则 p:first-child 指的是#p1,
因为它意为:选择p元素,它是属于其父元素的第一个子元素

6.2 伪元素

伪元素因不存在于HTML文档中,故称为伪元素,与真正的元素区分开来

常用伪元素

::before ::after

注意:
1.以上两种伪元素在使用时,必须设置content属性,即使是空字符串 content:’’;也可以,否则是不会显示出来的
2.伪元素的初始位置是存在于真正的元素边框内部的


选择器权重

当出现多个选择器,或在使用UI框架想覆盖框架的样式时,选择器权重的知识成为关键。

1.特殊性

特殊性分为四个等级,越在前面就越高,当规则冲突时,特殊性最高的选择器胜出。

  1. 内联样式: 1,0,0,0
  2. ID选择器: 0,1,0,0
  3. 类选择/属性选择/伪类: 0,0,1,0
  4. 元素选择器/伪元素: 0,0,0,1
    另: 结合符(> + ~)/通配符(*)不增加权重

来看一些例子帮助理解(为了直观,CSS代码写成一行,请勿在实践中模仿):

1
2
3
4
5
6
7
8
9
10
11
h1{color:white;} 0,0,0,1

header h1{color:red;} 0,0,0,2

h1.black{color:black;} 0,0,1,1

div > input[type="text"]{color:green;} 0,0,1,2

#list:hover {color:blue;} 0,1,1,0

div::after(style="color:wheat;") 1,0,0,2

以上例子,越往下,选择器特殊性越高

2.重要声明

难道面对内联样式,不使用js,难道真的无法修改了吗?
不,我们还可以使用重要声明,即在普通声明的结束分号之前加上!important

1
span.important
1
2
3
.important{
color: gray !important;
}

面对普通声明,重要声明总是胜出
重要声明一共有5级,权重由大到小排列如下:
1.读者的重要声明
2.开发人员的重要声明
3.开发人员的正常声明
4.读者的正常声明
5.用户代理的默认样式

3. 继承

基于继承机制,样式不仅应用到指定的元素,还会应用到它的后代元素
值不会向上传播.
只有一个例外:
body元素的背景样式可以传递到html元素,相应地可以定义画布.

继承的值根本没有特殊性,也即比0还要小.这一点绝不能等闲视之
这到底是什么意思呢?考虑下面的情况:

1
2
div.article.font52
p.title 标题

根据继承, p 元素继承了div的font52这个类,即字体大小为52px,
但由于使用的框架预设定了样式

1
2
3
p {
font-size: 14px;
}

虽然权重很小,却也比继承的权重要大,因而元素 p的字体大小为14px

这就提醒我们:谨慎地使用通配符,小心地使用元素选择器,还有,不要太相信继承

另:
不能继承的值: 盒子模型(margin,padding,background), border

疑问:
但在实践中,我们会发现,如果设置了父元素的背景颜色,
如果不设置子元素的背景颜色,子元素背景色是与父元素的一致的,这难道不是继承?

使用控制台,查找子元素的样式规则,你的确找到不从父元素中继承的background-color属性。它其实是继承了来自浏览器的默认背景色:transparent(透明),因而看起来背景色与父元素的背景色一致。但这真不是继承。

4. 层叠

根据上面的知识,现在提出一个问题:
如果特殊性相同的选择器选中了同一个元素,那将会怎样呢?
“层叠样式表”这个名字可能会提供一点提示。CSS所基于的方法通过结合继承与特殊性,让样式层叠到一起的。其规则如下:

  1. 找出作用于给定元素的所有选择器
  2. 按声明重要性排序。声明来源有:开发人员,读者,用户代理(可简单理解为浏览器)默认样式。重要性声明胜出。
  3. 按特殊性排序。特殊性高的胜出。
  4. 按出现选择器出现顺序排序。一个声明出现在文档中越后面,则其权重越大。如果样式表中有导入样式表,一般认为出现在导入样式表中的声明在前,主样式表中的所有声明在后,也即权重越大。

以上是实践中会用到的CSS基础,希望能对大家有所帮助。

Fork me on GitHub