# 选择符

# 样式的基本规则

# 元素选择符

通常是 HTML 选择符,HTML文档中的元素是最基本的选择符。

html{}
div{}
1
2

# 声明和关键字

{} 表示规则的声明块,可以有多条样式声明,声明格式为 属性: 值;

属性或值有误则本条声明将被忽略。

属性的值如果可以是多个关键字,通常以空格分隔,有时以斜线(/)分隔。

属性值中可以使用斜线的有:

  • font
  • background
  • border-image
  • border-radius
  • grid
  • grid-area
  • grid-column
  • grid-row
  • grid-template
  • mask-border

以逗号分隔的有:声明多个背景图像,过渡属性和阴影时,以及函数参数比如线性渐变 linear-gradient() 和变形函数 translate();

.box {
  box-shadow: inset -1px -1px white, 3px 3px 3px rgba(0, 0, 0, 0.2);
  background-image: url(myimage.png), linear-gradient(180deg, #fff 0%, #000 100%);
  transform: translate(100px, 200px);
}
a:hover {
  transition: color, background-color 200ms ease-in 50ms;
}
1
2
3
4
5
6
7
8

# 群组

# 群组选择符

以逗号分隔多个选择符,逗号告诉浏览器,后面的规则要应用到这群选择符中。

h2, p, div {}
1

# 通用选择符 *

匹配所有元素,但通用选择符虽然方便,但是它的特制度是 0-0-0 ,而且由于目标是一切元素,可能会出现一些意想不到的后果。

# 在旧的浏览器中使用新元素

比如 IE8 中不识别 <main> 元素,可使用如下 js 代码让 IE8 知道此元素的存在。

document.createElement('main');
1

运行后,IE8 将匀速选择并为其添加样式。

# 类选择符和 ID 选择符

# 类选择符

元素设置 class 属性,属性值之前加上点(.)就是类选择符。

<p class="para"></p>
1
.para {
  color: red;
}
1
2
3

元素选择符结合类选择符

p.para {
  color: red;
}
1
2
3

ID、类、属性、伪类、伪元素选择符之前如果没有元素选择符,默认为通用选择符。则作用到所有包含此类选择符的元素上。

*.para {
  color: red;
}
1
2
3

# 多个类

一个元素可以包含多个类,以空格分隔,并且不分先后顺序。

<div class="color size"></div>
1
.color {
  color: red;
}
.size {
  font-size: 16px;
}
1
2
3
4
5
6

# ID 选择符 #

元素设置 id 属性,属性值前加上 # 号就是 id 选择符。

<p id="para"></p>
1
#para {
  color: red;
}
1
2
3

# 属性选择符

之前的 id 选择符和类选择符都是选择的元素属性的值。属性选择符根据元素的属性来选择。

大致分为:简单属性选择符,精准属性选择符,部分匹配属性值选择符,起始值属性选择符。

# 简单属性选择符

选择具有某个属性的元素,而不管属性的值是什么。

/* 具有 class 属性的元素就会被选择 */
div[class] {
  color: red;
}

/* 所有具有 title 属性的元素都会被选择 */
[title] {}

/* 同时具有多个属性才被选择 */
a[href][title] {}

1
2
3
4
5
6
7
8
9
10
11

# 精准属性选择符

元素不但具有某个属性,还需要属性值匹配才选择。

div[class="warning"] {}

/* 属性值带空格时,选择符中必须保持一致 */
div[class="warning color"][title="hello"] {}
1
2
3
4

# 部分匹配属性值选择符

  • foo|="bar"foo 属性值,属性值就是 bar 或以 bar- 开头则会被选中
  • foo~="bar"foo 属性值,属性值以空格分隔的一组词,其中某个词是 bar 即可匹配
  • foo*="bar"foo 属性值,属性值中包含 bar 即可匹配
  • foo^="bar"foo 属性值,属性值以 bar 开头
  • foo$="bar"foo 属性值,属性值以 bar 结尾

# |=

如果开发一个 CSS 框架或库,没必要提供 btn btn-small btn-arrow btn-active 这样冗长的类。

<button class="btn-small-arrow-active">Click Me</button>
1
*[class|='btn'] {
  border-radius: 5px;
}
1
2
3

# ~=

<p class="urgent warning">hello</p>
1
p[class~='warning'] {
  color: red;
}
1
2
3

其实此写法与普通类选择符是等效的。比如 p.warningp[class~="warning"] 等效。

那么为何除了类选择符,还需要这样的选择符呢?因为除了类以外的其他属性还可以使用,比如 title 属性。

img[title~='Figure'] {
  border: 1px solid gray;
}
1
2
3

如果多个元素都有 title 属性,并且 title 中都包含 Figure 这个词,则均可匹配。

# *=

比如,希望选中所有链接地址中包含 abc.com 的元素

a[href*='abc.com'] {
  font-weight: bold;
}
1
2
3

# ^=

匹配以某个字符串开头的属性值

img[alt^='Figure'] {}
1

# $=

匹配以某个字符串结尾的属性值

img[src$='.jpg'] {}
1

# 不区分大小写的标识符

CSS Selectors Level 4 为属性选择符引入了一个不区分大小写的选项。在结束的方括号前加上 i

比如,选择所有指向 PDF 文档的链接,但不确定是 .pdf 还是 .PDF ,可以写成:

a[href$='.PDF' i]
1

注意,这个 i 只针对属性值,不针对属性名。

# 根据文档结构选择

# 后代选择符

多个选择符使用空格连接,空格后的选择符是空格前选择符的后代,样式只作用在最后一个指定的后代身上。

div p {}

div p span em {}

1
2
3
4

注意,后代元素的层级间隔可以是无限的,并且它对元素的距离一无所知。两个元素的距离对是否应用样式规则没有影响。遇到特指度和相互抵消规则时这一点很重要。

<div class="outer">
  <div class="inner">
    <span>hello</span>
  </div>
</div>
1
2
3
4
5
.inner span {
  color: blue;
}
.outer span {
  color: blue;
}
1
2
3
4
5
6

最终应用的颜色是蓝色,虽然 .outerspan 更近,但后代元素无感。

# 子元素选择符 >

不想选择任意层级后代元素,只想选择下一层级的子元素时使用。

<div>
  <p></p>
</div>
1
2
3
/* 只选择 div 下的 直接子元素 p 元素 */
div > p {}
1
2

# 选择后续相邻兄弟元素 +

选择紧跟在某一个元素之后的某个同级元素。

<div></div>
<p></p>
1
2
div + p {}
1

# 选择后续不管是否相邻的兄弟元素 ~

Selectors Level 3 引入的后续兄弟选择符 ~

```html
<div></div>
<p></p>
<a></a>
<p></p>
<span></span>
<p></p>
1
2
3
4
5
6
7
div ~ p {}
1

将会把 div 之后的不管是否相邻的所有同级的 p 元素都选中。

# 伪类选择符 :

可以为文档中不一定真实存在的结构指定样式,或者为某些元素(甚至文档本身)的特定状态赋予幽灵类。

# 拼接伪类

/* 鼠标悬停在未访问过的连接上时 */
a:link:hover {
  color: red;
}

/* 鼠标悬停在已访问过的连接上时 */
a:visited:hover {
  color: maroon;
}
1
2
3
4
5
6
7
8
9

(伪类的)顺序无关紧要,a:hover:linka:link:hover 的效果是一样的。

同样的,可以为特定语言的不同状态的链接设置不同的样式,例如德语:

a:link:hover:lang(de) {
  color: gray;
}
a:visited:hover:lang(de) {
  color: silver;
}
1
2
3
4
5
6

注意,不要拼接相互排斥的伪类,比如,一个链接不可能既是已访问又是未访问。a:link:visited 没有任何意义,匹配不了任何东西。

# 结构伪类

注意,伪类始终指代的是所依附的元素。

比如 div:first-childdiv > :first-child, 前者选中的而是 div 元素,而且这个 div 元素 必须是它的父元素中的第一个元素才会被选中。后者选中的是 div 元素中的 第一个子元素。

# 选择根元素 :root

在 HTML 中,根元素是 <html>,但在其他比如 XML 中,根元素则不是。

:root {}
1

以上则可选中根元素,在 HTML 中它则会选中 <html> 元素。

# 选择空元素 :empty

元素只有真的为空才会被选中,没有空白,没有可见内容(如文本),没有子元素。

如果使用 *:empty { display: none; } 禁止显示所有空段落,但这里有个问题,imginput 这样的空元素也会被匹配,也就是说, img{}img:empty{} 其实是一样的匹配效果,只是权重不同而已。

截止2017年末,:empty 是唯一一个在匹配元素的时候考虑文本节点的 CSS 选择器。所有 Selectors Level 3 的其他选择器都只考虑元素节点,而完全忽略文本节点——例如,前面讨论过的兄弟组合器

# 唯一子元素 :only-child / 唯一类型元素 :only-of-type

/* img 元素如果是父元素中的唯一子元素则会被选中 */
img:only-child {border: 1px solid black;}

/* 选择 a 元素的任意层级后代中,img 是唯一子元素的时候,选中 img 元素 */
a[href] img:only-child {border: 2px solid black;}
1
2
3
4
5

以下三种情况都可以被选中:

<a href="http://w3.org/"><img src="w3.png" alt="W3C"></a>

<a href="http://w3.org/"><span><img src="w3.png" alt="W3C"></span></a>

<a href="http://w3.org/">
  A link to
  <span>
    the
    <img src="w3.png" alt="">
    web
  </span>
  site
</a>
1
2
3
4
5
6
7
8
9
10
11
12
13

如果是写为 a[href] > img:onlychild 则只会匹配以上的第一种情况。

唯一类型元素 :only-of-type

<a href="http://w3.org/"><b></b><img src="w3.png" alt="W3C"></a>
<a href="http://w3.org/"><span><b></b><img src="w3.png" alt="W3C"></span></a>
1
2
a[href] img:only-of-type {border: 5px solid black;}
1

img 作为所有兄弟元素中的唯一的元素类型时,则被选中。

注意,此处的唯一类型指的是唯一的元素类型。

# (倒数)第一个子元素 :first-child / :last-child

<div>
  <p></p>
  <span></span>
  <a></a>
</div>
1
2
3
4
5
/* 当 p 元素是父元素的第一个子元素时选中 p 元素 */
p:first-child {}

/* 当 a 元素是父元素中的最后一个子元素时选中 a 元素 */
a:last-child {}

/* :first-child 和 :last-child 一同使用就等效于 :only-child */
p:first-child:last-child{}
/* 等效于 */
p:only-child{}
1
2
3
4
5
6
7
8
9
10

# (倒数)第一个某类型元素 :first-of-type / :last-of-type

:first-of-type 从前往后查找在父元素中第一次出现的某个类型的元素。

:last-of-type 从后往前倒序查找在父元素中第一次出现的某个类型的元素

<div>
  <p></p>
  <span></span>
  <a></a>
  <div></div>
</div>
1
2
3
4
5
6
/* 选中父元素中第一次出现的 span 元素 */
span:first-of-type {}

/* 选中父元素中倒数第一次出现的 a 元素 */
a:last-of-type {}
1
2
3
4
5

# (每)(倒数)第 n 个子元素 :nth-child(n) / :nth-last-child(n)

选中(倒数)第 n 个子元素,n 可以为任意整数。

:nth-child(1) 就等同于 :first-child

:nth-last-child(1) 就等同于 :last-child

括号中还可以使用公式: :nth-child(xn):nth-child(xn ± y) ,其中 x 和 y 是具体的整数,n 就是照写,±b 可选。:nth-last-child(n) 用法相同。

/* 选中父元素中偶数位出现的 p 元素 */
p:nth-child(2n) {}
/* 关键字形式的 偶数位 */
p:nth-child(even) {}

/* 选中父元素中奇数位出现的 p 元素 */
p:nth-child(2n + 1) {}
/* 关键字形式的 奇数位 */
p:nth-child(odd) {}


/* 选中父元素中第 5 个元素之后的 p 元素 */
p:nth-child(n + 5) {}
/* 选择效果相同,权重更高的表示方法 */
p:nth-child(4) ~ p {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* 如果父元素中只有一个 li 元素,则宽度为 100% */
li:only-child {width: 100%;}

/* 是第一个且是倒数第二个,是第二个且是倒数第一个,则宽度分别为 50% */
li:nth-child(1):nth-last-child(2),
li:nth-child(2):nth-last-child(1) {width: 50%;}

/* 是第一个且是倒数第三个,是第一个且是倒数第三个之后的所有 li 元素,宽度分别问 33.33% */
li:nth-child(1):nth-last-child(3),
li:nth-child(1):nth-last-child(3) ~ li {width: 33.33%;}

/* 是第一个且是倒数第四个,第一个且是倒数第四个之后的所有 li 元素,宽度分别问 25% */
li:nth-child(1):nth-last-child(4),
li:nth-child(1):nth-last-child(4) ~ li {width: 25%;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# (每)(倒数)第 n 个某类型子元素 :nth-of-type() / :nth-last-oftype()

用法基本和 :nth-child(n) / :nth-last-child(n) 相同,唯一的区别是此处会按照类型来计数。

# 动态伪类

动态伪类可以用于任何元素。以下只是列出经常用于 a 元素上。

a:link 应用到具有 href 属性,且未被访问过的 a 元素上

a:visited 应用到已被访问过的 a 元素上,但出于安全考虑,可设置的样式有限制。隐私保护限制只能对已访问链接设置颜色相关的属性,其他属性会被忽略。

a {}a:link {} 看起来相同,但前者无论有没有 href 属性都会选中,后者必须有 href 属性才会被选中。

# 用户操作伪类 :focus / :hover / :active

:focus 选中获得焦点的元素

:hover 选中鼠标指针悬停所在的元素

:active 选中激活的元素,比如鼠标按下的时候的那个元素

以上这些伪类常用于超链接,比如:

a:link {color: navy;}
a:visited {color: gray;}
a:focus {color: orange;}
a:hover {color: red;}
a:active {color: yellow;}
1
2
3
4
5

伪类的顺序很重要,通常是 link-visited-hover-active,后来改为了 link-visited-focus-hover-active。后文解释为什么要注重顺序,以及那些场景可以更改使用顺序。

当想把文档中所有鼠标悬停的元素文本都更改颜色时,可以设置:

body *:hover{
  color: red;
}
1
2
3

# 动态样式引起的问题

例如,可以将访问和未访问的链接设置为一种字体大小,并将其悬停链接设置较大的字体。

a:link, a:visited {font-size: 13px;}
a:hover, a:active {font-size: 20px;}
1
2

当鼠标指针悬停在锚上时,用户代理会增加锚的大小。 或者,由于 :active 设置,当用户在触摸屏上触摸它时。

支持此行为的浏览器必须在锚点处于悬停状态时重绘文档,这可能会迫使对链接后面的所有内容进行重排。

# UI 状态伪类

  • :enabled 允许输入的UI元素(例如表单中的input)
  • :disabled 不允许输入的UI元素(例如表单中被禁止输入的input)
  • :checked 已经被选中的单选或复选框,无论是文档自身选中的还是用户点击选中的
  • :indeterminate 没有被选中的单选或复选框,此状态只能由 DOM 脚本设定,不能由用户触发
  • :default 被默认选中的单选、复选框或下拉框
  • :valid 用户输入合法数据的元素
  • :invalid 用户输入不合法数据的元素
  • :in-range 用户输入的数据在指定大小(min 和 max)范围内的元素
  • :out-of-range 用户输入的数据不在指定大小范围内的元素
  • :required 要求用户必须输入数据的元素
  • :optional 不强制要求用户必须输入数据的可选元素
  • :read-write 可编辑元素
  • :read-only 只读元素

# 锚点伪类 :target

当 URL 包含片段标识符时,它所指向的文档被称为目标(在CSS中)。因此,可以使用 :target 伪类唯一地设置作为 URL 片段标识符目标的所有元素的样式。

比如访问的页面为 http://www.w3.org/TR/css3-selectors/#target-pseudo,这个URL的用井号分割的伪部分就是片段标识符。如果引用的页面(http://www.w3.org/TR/css3-selectors/)具有ID为target-pseudo 的元素,则该元素将成为片段标识符的目标。

:target 样式不会在这两种情况下应用:

  1. 如果页面是通过没有片段标识符的URL访问的

  2. 如果通过具有片段标识符的URL访问该页面,但是该标识符与文档中的任何元素都不匹配

但是,更有趣的是,如果文档中的多个元素可以由片段标识符进行匹配,例如,如果作者在同一文档中错误地包含了三个单独的 <div id ="target-pseudo"> 实例,会发生什么?

CSS 无需解决此问题,主要取决于浏览器会选择一个还是三个元素,CSS 会将 :target 样式都应用于浏览器选中的有效目标。

# 语言伪类 :lang

/* 使用语言伪类设定样式 */
*:lang(fr) {font-style: italic;}

/* 使用属性选择符设定样式,元素必须设置了 lang 属性才能匹配 */
*[lang|="fr"] {font-style: italic;}
1
2
3
4
5

通常,在同样的选择时,伪类选择符比属性选择符更可靠。

# 否定伪类 :not()

:not() 伪类依附在某个元素上,括号中是简单的选择符。

简单选择符,指的是没有 祖辈 - 后代 关系的的选择符,比如元素、通用、属性、类、ID或伪类选择符等。

虽然括号中放通用选择符是合法的,但其实是没有意义的。比如 p:not(*) 选择不是任何元素的 p 元素。

另外,元素选择符的否定伪类括号中放元素选择符也没有意义,比如 p:not(p)p:not(div)

注意,:not() 的括号中只能使用一个选择符,不能使用群组,连接符,后代选择符等。

/* 选择不是 section 元素的元素之中的所有 table 元素 */
*:not(section) > table

/* 选择不是表头元素 thead 的元素之中的 tr 中的 th 元素 */
*:not(thead)> tr > th
1
2
3
4
5

:not() 伪类不能嵌套使用,比如 p:not(:not(p)) ,括号中也不能使用伪元素,因为不是简单选择符。

:not() 可以串在一起使用,相当于 “也不是” 的意思。

*.link:not(li):not(p) {font-style: italic;}
1

# 伪元素选择符

在 CSS 2 中都伪类和伪元素都使用一个冒号,但现在伪元素改为了使用一对冒号,比如 ::first-line 。 应该始终保持使用一对冒号 ::

注意,所有伪元素只能出现在选择符的最后,比如 p::first-line em {} 是无效的。这也表明一个选择符只能有一个伪元素。

# 首字母伪元素 ::first-letter

选择任何非行内元素中文本的首字母,首字母前后的标点符号也会被包含选中。

常用于排版中的首字母大写或首字母下沉效果。

# 首行伪元素 ::first-line

用于选中元素中文本的的第一行。

#::first-letter::first-line 的限制

目前,只能用于块级元素上,例如 divp 等。不能应用到行内元素上。

允许伪元素使用的CSS属性

::first-letter ::first-line
All font properties All font properties
All background properties All background properties
All text decoration properties All margin properties
All inline typesetting properties All padding properties
All inline layout properties All border properties
All border properties All text decoration properties
box-shadow All inline typesetting properties
color color
opacity opacity

# 前置、后置伪元素 ::before / ::after

/* 在 h2 元素之中,加入前置内容(此处为两个方括号),并将颜色设为银色 */
h2::before {
  content: "]]";
  color: silver;
}

/* 在 body 元素中,加入后置内容(此处为一段文本) */
body::after {
  content: "The End.";
}
1
2
3
4
5
6
7
8
9
10

之后会对 ::before::after 进行更深入的研究。