一、选择器种类
1. 通配选择器
通配选择器用 *
符号来表示,它选中在当前限定条件范围下的所有元素。
CSS代码示例:
/* 选中html文档中的所有元素 */
* {}
/* 选中div元素中的所有后代元素 */
div * {}
通配选择器常常用来先机清除元素的默认样式,这样在后续代码中书写样式时不至于受默认样式的影响。
比如
ul
、li
列表默认会有一个小圆点,body
默认有margin
值使得设置背景色时外围有空隙。
2. ID选择器
ID选择器以 #
符号开头,配合HTML元素的id属性来使用,以 #xxx
的形式选中id值为 xxx
的HTML元素,由于id值是唯一的(当然可能有人粗心大意给不同元素设置了相同的id值,此时按先后顺序只会匹配第一个有此id值的元素),所以id选择器总是能精准地匹配到目标元素,不可能匹配多个符合条件的元素。
HTML代码示例:
<div id="container">
<button id="submit">提交</button>
</div>
CSS代码示例:
/* 选中id为container的元素,也就是上述配套HTML代码中的div */
#container {}
/* 选中id为submit的元素,也就是上述配套HTML代码中的button */
#submit {}
/* 选中 id为container的元素的后代中id为submit的元素 */
/* 但是这个组合选择器没有意义,因为只使用单个id选择器就能够精确匹配 */
#container #submit {}
3. 类选择器
类选择器以 .
符号开头,配合HTML元素的class属性来使用,以 .xxx
的形式选中class值含有 xxx
的HTML元素。
HTML代码示例:
<p class="content first">这是第一段内容</p>
<p class="content">这是第二段内容</p>
CSS代码示例:
/* 选中class中含有content的元素,上述配套HTML代码中的2个p标签都符合条件 */
.content {
font-size: 20px
}
/* 选中class中含有first的元素,上述配套HTML代码中第1个p标签符合条件 */
.first {
color: red;
}
4. 元素选择器
以HTML标签名来作为选择器。
CSS代码示例:
/* 选中所有div元素 */
div {}
/* 选中所有input元素 */
input {}
5. 属性选择器
选中具有特定属性的HTML元素,以 [属性名]
或 [属性名=属性值]
等形式配合特定属性名。
5.1 属性选择器基本示例
HTML代码示例:
<p title="text">一段文字</p>
<img src="tiga.jpg" alt="奥特曼">
<a href="http://baidu.com">去百度</a>
CSS代码示例:
/* 选中所有具有title属性的元素 */
[title] {}
/* 选中所有 alt属性值为 奥特曼 的元素 */
[alt="奥特曼"] {}
/* 同上一个选择器的作用,只不过省略了双引号(推荐写上双引号) */
[alt=奥特曼] {}
5.2 属性选择器写法形式
写法形式 | 匹配解说 | 示例 |
---|---|---|
[attr] | 元素具有 attr 属性即可,不必关心该属性具体是什么值 | [title] |
[attr=value] | 元素 attr 属性值必须和指定的 vlaue 相同(即全等) | [alt="apple"] |
[attr^=value] | 元素 attr 属性值必须以 vlaue 开头(包括全等) | [href^="http"] |
[attr$=value] | 元素 attr 属性值必须以 vlaue 结尾(包括全等) | [href$=".com"] |
[attr*=value] | 元素 attr 属性值中的某一段字符串和 vlaue 相同 | [href*="www"] |
[attr~=value] | 元素 attr 属性有一个或多个值(以空格隔开,比如class属性就支持指定多个值),只要其中一个值和 vlaue 相同即可 | [class~="row"] |
[attr|=value] | 元素 attr 属性和 vlaue 相同,或者以 value- 开头(注意别漏了连字符 - ,这个也包括全等的情况) | [lang|="zh"] |
6. 伪类选择器
伪类用于选择处于特定状态的元素,它的一个特点是以单个冒号 :
开头。
HTML元素的一些常见状态比如超链接 <a>
元素的未访问、已访问状态,输入框 input
元素的聚焦状态等等。
具体有哪些伪类请参考附录。
6.1 简单示例
HTML代码示例:
<a href="http://jd.com">去购物</a>
<input type="text">
<ul>
<li>足球</li>
<li>篮球</li>
<li></li>
</ul>
<div></div>
CSS代码示例:
/* 超链接a标签指向的网站未被访问过的样式(通常是a标签才有该伪类,所以也可写作 a:link)*/
:link {
color: #ecc6c6;
}
/* 超链接a标签指向的网站已被访问过的样式(通常是a标签才有该伪类,所以也可写作 a:visited)*/
:visited {
color: #baf6b8;
}
/* 元素获得焦点时的样式(通常是表单元素才有该伪类,所以也可写作 input:focus)*/
:focus {
background-color: #fbdafd;
}
/* 选中没有内容的空div元素(内部连空格也不能有,这个伪类许多标签都支持) */
div:empty {
height: 40px;
width: 40px;
background-color: #9bbbec;
}
/* 选中li元素,但这个li必须是其父元素的第一个子元素 */
li:first-child {
color: blueviolet;
}
6.2 助记
等学习了伪元素后,非常容易和伪类记混淆了,所以为了帮助记忆请牢牢记住以下规则:
功能:伪类用于选择处于特定状态的元素;
作用范围:依然是一整个元素,元素内部样式表现一致。
当你觉得需要添加一个什么class来标记该元素以便书写样式时,比如访问过的超链接标签,可以手动给这些a
标签加上class来单独控制样式,再比如当前正在编辑的input
,如果我们能提前知道要编辑的是哪个input
就可以给该输入框加特定class然后书写样式。
总之你觉得可以加个class类给你想要改变样式的元素,就可以考虑是伪类。
7. 伪元素选择器
伪元素用于选择元素内部的一些特殊位置,它是以两个冒号 ::
开头。
注意:早期的css版本没有严格区分伪类和伪元素,且伪类和伪元素的种类也没有这么多,所以都是以单个冒号开头,后面的版本才规定伪元素以两个冒号开头,所以在书写部分伪元素选择器时,以单个冒号开头浏览器也能识别,这是浏览器为了兼容性而保留的特性支持,建议都以最新标准来写代码
HTML元素的内部的一些特殊位置,比如正式内容的首行位置、正式内容前的位置、正式内容后的位置等等。
具体有哪些伪元素请参考附录。
7.1 简单示例
HTML代码示例:
<p>Nunquam examinare extum.Ubi est audax rumor? Sensor, hypnosis, and definition. Tolerare patienter ducunt ad emeritis domina.</p>
<ul>
<li>1999</li>
<li>3728</li>
</ul>
CSS代码示例:
/* 选中p元素内部内容第一行的位置 */
p::first-line {
background-color: #e7b1b1;
}
/* 选中li元素内部内容之前的位置 */
li::before {
content: "$";
}
/* 选中li元素内部内容之后的位置 */
li::after {
content: ".00";
}
7.2 助记
为了不与伪类记混淆了,所以请牢牢记住以下规则:
功能:伪元素用于选择元素内部特定的位置;
作用范围:元素内部再细分位置,不同位置分别控制样式。
当你觉得需要添加一个什么标签包裹特定内容以便书写样式时,比如上面选中p
元素内部内容的第一行单独设置样式,在浏览器窗口宽度固定的情况下我们可以观察出哪些内容在第一行,从而在源代码中把这第一行的内容单独用一个span
包裹起来然后设置样式,又如上面选中li
标签内容的前后位置加上币种符号和小数点使得数字文本表现得更像价格一样,也可以在手动在HTML结构中前后各加一个span
标签用来放置币种符号和小数点。
总之你觉得可以加个元素包裹内容从而便于设置样式,就可以考虑是伪元素。
8. 关系选择器
关系选择器不是基本选择器,它们只用来表示一种关系,多数情况下是借助一组符号突出强调选择器之间的关系,它们无法单独使用,只能和其他选择器一起组成复合选择器。
8.1 后代关系
两个选择器之间使用 空格 隔开即形成了祖先和后代的关系(多个空格视为一个空格,多余的空格相当于只起调整代码风格的作用,不能存在其他符号,否则就不是后代关系了而可能是其他关系)。
HTML代码示例:
<div>
<span>没选中</span>
<ul>
<li><span>选中</span></li>
<li>没选中</li>
</ul>
</div>
CSS代码示例:
/* li和span是ul的后代,span是li的后代,*/
/* 最终选中的是span元素,其必须有li作为祖先,且该li必须要有div作为祖先 */
div li span {}
8.2 直接子代
两个选择器之间使用右尖括号 >
隔开,前者和后者即形成了父元素和直接子元素的关系,两者直接不能再有其他HTML层级结构,否则后者就不是前者的直接子代,而仅仅是后代(可以有调整代码格式的空格存在,下同)。
HTML代码示例:
<div>
<span>选中</span>
<div>
<span>选中</span>
</div>
<ul>
<li><span>没选中</span></li>
</ul>
</div>
CSS代码示例:
/* 目标span必须是div的直接子元素,嵌套了多级结构的后代span元素不符合要求,*/
/* 最终选中的是span元素,其必须有div作为直接父元素 */
div > span {}
8.3 邻接兄弟
两个选择器之间使用加号 +
隔开,后者就是前者的邻接兄弟,中间不能再有其他不匹配的元素,同时该选择器在HTML结构中只会向后看,不会向前看,即前者的上一个兄弟始终不会被选中,虽然在结构上是紧紧相连的。
HTML代码示例:
<div>
<span>不选中</span>
<p>不选中</p>
<span>选中</span>
<span>不选中</span>
</div>
CSS代码示例:
/* 目标span必须是p之后的第一个兄弟元素,*/
/* 最终选中的是span元素,和其紧紧相连的前一个兄弟元素必须是p元素 */
p + span {}
8.4 通用兄弟
两个选择器之间使用波浪号 ~
隔开,它选中是前者兄弟的后者,该选择器在HTML结构中也只会向后看,不会向前看,和邻接兄弟选择器相比,就是解开了紧紧相连这个限制条件。
HTML代码示例:
<div>
<span>不选中</span>
<p>不选中</p>
<span>选中</span>
<span>选中</span>
</div>
CSS代码示例:
/* 选中的span必须是p之后的兄弟元素,最终选中的是span元素 */
p ~ span {}
8.5 并集
多组选择器之间用英文逗号 ,
隔开,选中至少满足其中一个条件的元素,这些选择器选中的元素共用这个样式声明块。
CSS代码示例:
/* body、h1元素,以及class为box、id为caption的元素共用一个样式声明块 */
body, h1, .box #caption {}
8.6 交集
多个选择器之间紧挨着写(注意两个元素选择器不能连写),表示选中同时满足这些选择器条件的元素。
/* 选中div元素,且该div的class属性具有box这个样式类 */
div.box {}
/* 选中处于鼠标悬停状态的h1元素,且该h1必须同时有box和container这个两个样式类 */
h1.box.container:hover {}
9. 复合选择器
复合选择器不是一个基本选择器,它是通过基本选择器以及关系符合来组成的选择器。
例如:
.clearfix:after, .clearfix:before {}
.calendar .header .text > span {}
.table .row.head {}
[data-theme="feb"] .table .row .cell.active span{}
二、样式优先级
设想有这样一种情况,两个不同的选择器选中了同一个元素,这个两个选择器的样式声明块中如果规定的样式名都不同时,自然都生效,如果有相同的样式名,且该样式名设置了不同的样式值,这时候就有冲突了。
冲突的样式名到底渲染哪个选择器指定的样式值其实是可以推算的,也就是计算选择器的权重。
在学习计算权重的规则前,首先聊一聊样式书写的位置对优先级的影响。
1. 样式位置
HTML文件中有3个位置可以编写样式代码或者关联css样式,分别是:
1.1 行内样式
直接写在HTML标签的style属性内的样式。
<span style="color: red;">行内样式</span>
1.2 内部样式
写在 style
标签内的样式(通常情况 style
标签放在 head
标签下,不过这不是强制的)。
<head>
<title>内部样式</title>
<style>
h1, h2, p {
margin: 0px;
}
</style>
</head>
1.3 外部样式
通过 link
标签关联的外部CSS文件。
<head>
<title>外部样式</title>
<link rel="stylesheet" href="./xxx.css">
</head>
2. 优先级规则
2.1 基本规则
- !important > 行内样式 > 内部样式 = 外部样式
- ID选择器 > 类选择器 > 元素选择器 > 通配选择器
- 同权重则后来者居上(以后规定的样式为准)
2.2 后来者居上
权重相同时(比如内部样式和外部样式,或内部样式同时有两个 h1
元素选择器),还遵循后来者居上的原则,即后出现的选择器规定的样式优先。
示例外部index.css文件:
h1 { color: red; }
示例HTML文件:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>探究样式优先级</title>
<!-- 先关联的外部样式文件 -->
<link rel="stylesheet" href="./index.css">
<!-- 后书写的内部样式style标签 -->
<style>
/* 外部样式的h1选择器规定的样式和内部样式有冲突,外部样式和内部样式虽然优先级相同,但根据后来者居上原则,外部样式文件对h1颜色样式的规定将失效 */
h1 {
color: blue;
border-radius: 2px;
}
/* 内部样式中又出现了一个h1选择器,根据后来者居上的原则,先前的h1选择器规定的颜色样式将失效,以这个h1选择规定的颜色样式为准 */
h1 {
color: yellow;
font-weight: bold;
}
</style>
</head>
<body>
<!-- h1元素的行内样式规定了font-weight样式,和内部样式中的规定冲突,行内样式优先 -->
<!-- 最终样式为 color: yellow; border-radius: 2px; font-weight: normal; -->
<h1 style="font-weight: normal;">标题</h1>
</body>
</html>
2.3 !important最优先
可以在某个具体的样式后面加 !important
来提高该样式优先级,且优先级将超越行内样式。
!important > 行内样式 > 内部样式 = 外部样式
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>探究样式优先级</title>
<style>
h1 {
/* !important提高了样式优先级 */
color: blue !important;
}
</style>
</head>
<body>
<!-- 最终样式为 color: blue; -->
<h1 style="color: red;">标题</h1>
</body>
</html>
3. 计算选择器权重
选择器的权重是以 (a, b, c)
的格式表示的,从左往右逐位开始比较数字大小,数字更大则该选择器优先级就更高,相同则继续比较下一位,所有位相同时则按照后来者居上原则判断样式优先级。
比如 .calendar .header .text > span
是 (0, 3, 1)
,它的优先级就没有 [data-theme="feb"] .row .cell.active span
的 (0, 4, 1)
高。
3.1 权重格式解读
权重的表示格式通常为 (a, b, c)
,各字母的含义是:
a
:ID选择器的个数;b
:类、伪类、属性选择器的个数;c
:元素、伪元素选择器的个数。
3.2 图示权重计算
附上别的大佬制作的图示(如有侵权请联系删除),当时我看了后觉得非常生动形象,一目了然。
在这张图片中,行内样式权重相当于
(1, 0, 0, 0)
,优先级高于其他选择器规定的样式,而带有!important
的样式权重则相当于(1, 0, 0, 0, 0)
,又扩展了1个高位数值,优先级更高。
3.4 类选择器重写
请看以下情况:
div.box.box {}
<div class="box"></div>
在这个例子中,div
的 class
属性指定了一个值为 box
,通常情况下使用 div.box
这个选择器就能精准匹配该元素,但是例子中却重复写了一个 .box
变成了 div.box.box
,这就是类选择器的重写,依然可以精准匹配该元素。
重写的类选择器是可以参与权重计算的,div.box
的权重是 (0, 1, 1)
,重写一次类选择器后,div.box.box
的权重是 (0, 2, 1)
,所以类选择器常用于在不增加元素 class
属性值的情况下提高这个选择器的样式权重。
类选择器不是只能重写一次,只要你愿意,重写100次也可以,样式权重也会相应提高。
3.5 权重位的进位
权重 (a, b, c)
在内存中不是分别保存的,而是同一个数字,假设保存权重数值的是一个24位的二进制数字,其中高八位存储a的值、中八位存储b的值、低八位存储c的值,那么当低位选择器的数量足够多时,可能向高位发生进位(和十进制数字满十进一是一个道理)。
实际情况也确实如此,早期的浏览器保存权重值的时候,每种同权重的选择器数量就是用的8位二进制存储的,当选择器数量超过2^8-1=255个时,就会发生进位,即 (0, 256, 1)
相当于 (1, 0, 1)
,将以下代码用IE浏览器运行,字体颜色将是蓝色,因为代码中的类选择器 .a
一共有256个,发生进位,权重和ID选择器 #a
相同,此时按照后来者居上原则,256个类选择器规定的样式优先级更高。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>权重位进位</title>
<style>
#a {
color: red;
}
.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a {
color: blue;
}
</style>
</head>
<body>
<div id="a" class="a">
红色 => id选择器获胜
<br>
蓝色 => 类选择器获胜
</div>
</body>
</html>
上述代码仅在IE浏览器中字体才为蓝色,换其他浏览器则还是红色,因为其他浏览器在不断的版本更新中扩大了存储权重的位数,这需要更多的类选择器才能发生进位,不过发生进位的临界数值实在太大,正常写代码是远远达不到临界值的。
附录
1. 伪类列表
选择器 | 描述 |
---|---|
:active | 在用户激活(例如点击)元素的时候匹配。 |
:any-link | 匹配一个链接的 :link 和 :visited 状态。 |
:blank | 匹配空输入值的``元素。 |
:checked | 匹配处于选中状态的单选或者复选框。 |
:current (en-US) | 匹配正在展示的元素,或者其上级元素。 |
:default | 匹配一组相似的元素中默认的一个或者更多的 UI 元素。 |
:dir | 基于其方向性(HTMLdir 属性或者 CSSdirection 属性的值)匹配一个元素。 |
:disabled | 匹配处于关闭状态的用户界面元素 |
:empty | 匹配除了可能存在的空格外,没有子元素的元素。 |
:enabled | 匹配处于开启状态的用户界面元素。 |
:first | 匹配分页媒体的第一页。 |
:first-child | 匹配兄弟元素中的第一个元素。 |
:first-of-type | 匹配兄弟元素中第一个某种类型的元素。 |
:focus | 当一个元素有焦点的时候匹配。 |
:focus-visible | 当元素有焦点,且焦点对用户可见的时候匹配。 |
:focus-within | 匹配有焦点的元素,以及子代元素有焦点的元素。 |
:future (en-US) | 匹配当前元素之后的元素。 |
:hover | 当用户悬浮到一个元素之上的时候匹配。 |
:indeterminate | 匹配未定态值的UI元素,通常为复选框。 |
:in-range | 用一个区间匹配元素,当值处于区间之内时匹配。 |
:invalid | 匹配诸如<input> 的位于不可用状态的元素。 |
:lang | 基于语言(HTML lang 属性的值)匹配元素。 |
:last-child | 匹配兄弟元素中最末的那个元素。 |
:last-of-type | 匹配兄弟元素中最后一个某种类型的元素。 |
:left | 在分页媒体 (en-US)中,匹配左手边的页。 |
:link | 匹配未曾访问的链接。 |
:local-link (en-US) | 匹配指向和当前文档同一网站页面的链接。 |
:is() | 匹配传入的选择器列表中的任何选择器。 |
:not | 匹配作为值传入自身的选择器未匹配的物件。 |
:nth-child | 匹配一列兄弟元素中的元素——兄弟元素按照an+b形式的式子进行匹配(比如 2n+1 匹配元素 1、3、5、7 等。即所有的奇数个)。 |
:nth-of-type | 匹配某种类型的一列兄弟元素(比如,<p> 元素)——兄弟元素按照an+b形式的式子进行匹配(比如 2n+1 匹配元素 1、3、5、7 等。即所有的奇数个)。 |
:nth-last-child | 匹配一列兄弟元素,从后往前倒数。兄弟元素按照an+b形式的式子进行匹配(比如 2n+1 匹配按照顺序来的最后一个元素,然后往前两个,再往前两个,诸如此类。从后往前数的所有奇数个)。 |
:nth-last-of-type | 匹配某种类型的一列兄弟元素(比如,<p> 元素),从后往前倒数。兄弟元素按照an+b形式的式子进行匹配(比如 2n+1 匹配按照顺序来的最后一个元素,然后往前两个,再往前两个,诸如此类。从后往前数的所有奇数个)。 |
:only-child | 匹配没有兄弟元素的元素。 |
:only-of-type | 匹配兄弟元素中某类型仅有的元素。 |
:optional | 匹配不是必填的 form 元素。 |
:out-of-range | 按区间匹配元素,当值不在区间内的的时候匹配。 |
:past (en-US) | 匹配当前元素之前的元素。 |
:placeholder-shown | 匹配显示占位文字的 input 元素。 |
:playing (en-US) | 匹配代表音频、视频或者相似的能“播放”或者“暂停”的资源的,且正在“播放”的元素。 |
:paused (en-US) | 匹配代表音频、视频或者相似的能“播放”或者“暂停”的资源的,且正在“暂停”的元素。 |
:read-only | 匹配用户不可更改的元素。 |
:read-write | 匹配用户可更改的元素。 |
:required | 匹配必填的 form 元素。 |
:right | 在分页媒体 (en-US)中,匹配右手边的页。 |
:root | 匹配文档的根元素。 |
:scope | 匹配任何为参考点元素的的元素。 |
:valid | 匹配诸如<input> 元素的处于可用状态的元素。 |
:target | 匹配当前 URL 目标的元素(例如如果它有一个匹配当前URL 分段的元素)。 |
:visited | 匹配已访问链接。 |
2. 伪元素列表
选择器 | 描述 |
---|---|
::after | 匹配出现在原有元素的实际内容之后的一个可样式化元素。 |
::before | 匹配出现在原有元素的实际内容之前的一个可样式化元素。 |
::first-letter | 匹配元素的第一个字母。 |
::first-line | 匹配包含此伪元素的元素的第一行。 |
::grammar-error | 匹配文档中包含了浏览器标记的语法错误的那部分。 |
::selection | 匹配文档中被选择的那部分。 |
::spelling-error | 匹配文档中包含了浏览器标记的拼写错误的那部分。 |