开篇介绍:
hello 大家,本篇博客我们来学习CSS中的选择器~
引言:为什么说 CSS 选择器是 CSS 的 "灵魂"
在前端开发的世界里,一直流传着一句话:"CSS 入门容易,精通难"。而这句话里的 "入门门槛",90% 都集中在 CSS 选择器上。
很多刚接触 CSS 的新手,都会遇到一个世纪难题:"我明明写了 CSS 样式,为什么浏览器里一点效果都没有?""为什么我给这个标签设置的样式,被别的样式盖掉了?""我只想改这一个元素的样式,怎么全页面的元素都跟着变了?"
遇到这些问题的时候,很多新手会下意识觉得 "我属性写错了""浏览器出 bug 了",但实际上,90% 的情况,都不是属性的问题,而是你的选择器写错了、没选对元素、优先级不够。
我们在之前的 CSS 基础文章里,给大家打过一个非常形象的比方:CSS 就像是给网页化妆的化妆师,而 CSS 属性(比如 color、font-size)就是口红、粉底、眼影这些化妆品。那 CSS 选择器是什么?它就是化妆师的眼睛和手------ 你得先精准找到要化妆的人,才能把化妆品涂到对应的位置上。如果你的眼睛瞎了、手歪了,找不到要化妆的人,哪怕你买了再贵的化妆品,也根本用不上。
再给大家举一个更贴近生活的例子,就像我们点外卖:
- 你点的外卖,就是你要设置的 CSS 样式;
- 外卖的收货地址,就是 CSS 选择器;
- 外卖员,就是浏览器。
如果你的收货地址写错了、写模糊了,外卖员要么找不到地方,把外卖送错了人,要么直接把外卖退回来,你的餐自然就吃不到了。同理,你的选择器写错了、写得不够精准,浏览器要么找不到你要修饰的元素,要么找错了元素,你写的样式自然就不会生效。
这就是 CSS 选择器的核心价值:它是连接 CSS 样式和 HTML 元素的唯一桥梁,是浏览器精准定位元素的唯一指令,是整个 CSS 体系的灵魂和根基。
没有选择器,你写的所有 CSS 属性都只是一堆无意义的字符,根本无法作用到页面上。
很多新手学习 CSS 的时候,总喜欢盯着各种花里胡哨的 CSS 属性,比如阴影、渐变、动画、Flex 布局,觉得 "学会这些就能写出好看的页面",却忽略了最基础、最核心的选择器。结果就是,属性背了一大堆,一动手写代码,样式要么不生效,要么全页面乱套,最后越学越懵,甚至放弃了前端学习。
所以,想要真正学好 CSS,第一步不是背多少属性,而是彻底搞懂 CSS 选择器------ 搞懂怎么精准找到你要修饰的元素,搞懂不同选择器的区别,搞懂选择器的优先级规则。只有把这个根基打牢了,你后面学的所有 CSS 属性、布局、动效,才能真正发挥作用。
一、CSS 选择器的核心分类:先搭好框架,再逐个攻破
在正式开始讲解每个选择器之前,我们先给大家搭好一个清晰的知识框架,让大家先对 CSS 选择器有一个整体的认知,不会学着学着就乱了套。
CSS 选择器发展到今天,从 CSS2 到 CSS3,官方定义了几十种不同的选择器,但是对于入门阶段的新手来说,我们不需要一下子掌握所有的选择器,只需要先掌握最核心、最常用、覆盖 99% 开发场景的选择器就够了。剩下的小众、高级的选择器,等你把基础打牢之后,再花 10 分钟就能学会,完全不用着急。
根据选择器的复杂程度和组合方式,我们把入门阶段必须掌握的选择器,分成两大核心类别,每个类别下面又细分了具体的选择器,和我们之前给大家的核心框架完全一致,但是这里会给大家补充每个分类的底层逻辑,让大家先理解为什么要这么分:
第一大类:基础选择器
基础选择器,顾名思义,就是 CSS 选择器里最基础、最原子化的单位,就像我们搭乐高的单个积木块,每一个选择器都是独立的,只靠自己就能完成元素的选择,不需要和其他选择器组合。
这是 CSS 选择器的根基,所有复杂的选择器,都是由基础选择器组合而来的。入门阶段,我们必须 100% 掌握 4 种基础选择器,按照使用频率和重要性排序分别是:
- 标签选择器(元素选择器):最基础的选择器,用 HTML 标签名直接选择元素;
- 类选择器:CSS 里最灵活、最常用、最重要的选择器,没有之一;
- id 选择器:唯一性最强的选择器,主要用于 JavaScript 交互,CSS 里要谨慎使用;
- 通配符选择器:最 "霸道" 的选择器,会选中页面里的所有元素,有固定的使用场景,不能滥用。
第二大类:复合选择器
复合选择器,就是把两个或多个基础选择器,按照固定的规则组合在一起形成的选择器。如果说基础选择器是单个积木块,那复合选择器就是把积木块搭成的造型,能实现更精准、更灵活、更复杂的元素选择,解决基础选择器解决不了的场景问题。
入门阶段,我们必须 100% 掌握 4 种复合选择器,按照使用频率和重要性排序分别是:
- 后代选择器(包含选择器):最常用的复合选择器,用于选中某个父元素里的所有后代元素;
- 子选择器:比后代选择器更精准,只选中父元素的亲儿子元素,不选孙子、重孙子;
- 并集选择器(集体声明):用于给多个不同的选择器设置相同的样式,大幅简化代码,减少冗余;
- 伪类选择器:重点掌握链接伪类选择器,用于给元素的不同状态设置样式,实现基础的交互效果。
除了这 8 种选择器之外,CSS3 还新增了很多高级选择器,比如属性选择器、结构伪类选择器(:nth-child () 等)、否定伪类选择器等等,这些我们会在入门之后的进阶内容里给大家详细讲解,这篇文章里我们只聚焦入门必备的 8 种选择器,绝对不会超纲,让大家一步一个脚印,把基础打牢。
二、基础选择器保姆级全解析:CSS 选择器的根基,一个都不能漏
2.1 标签选择器(元素选择器):给所有同名标签 "批量发通知"
2.1.1 核心定义:什么是标签选择器?
标签选择器,也叫元素选择器,是 CSS 里最简单、最基础的选择器,没有之一。它的核心定义非常简单:直接用 HTML 的标签名作为选择器,会选中当前 HTML 页面里,所有和这个标签名同名的 HTML 元素,给它们统一设置样式。
举个最简单的例子,你在 CSS 里写了p { color: red; },这里的p就是标签选择器,浏览器看到这个选择器之后,会把页面里所有的 <p>段落标签,不管它在页面的哪个位置,不管它里面有没有嵌套其他标签,全部找出来,把它们的文字颜色都改成红色。
2.1.2 生活化类比:一秒搞懂标签选择器的本质
为了让大家彻底理解标签选择器的本质,我们给大家举一个通俗易懂的生活化类比:
标签选择器,就像是学校里的年级统一广播通知。比如学校的广播里说:"请高一年级的所有同学,今天下午都穿蓝色校服来学校"。
这个广播通知里,有几个核心要素,和标签选择器完全对应:
- "高一年级" :就是我们 CSS 里的标签名 ,比如
p、div、h1; - "所有同学" :就是页面里所有同名的 HTML 标签,不管你是一班的、二班的,还是三班的,只要你是高一年级的,都要听这个通知;
- "今天下午都穿蓝色校服" :就是我们 CSS 里写的样式声明,也就是要给这些元素设置的样式规则。
这个广播通知,只会对高一年级的同学生效,对高二、高三的同学完全没影响;同理,标签选择器p,只会对页面里的<p>标签生效,对<div>、<h1>这些其他标签完全没影响。
而且这个通知是 "批量生效" 的,不用班主任一个一个班级去通知,广播一响,所有高一年级的同学都能收到;同理,标签选择器不用你给每一个<p>标签单独设置样式,只需要写一遍,所有的<p>标签都会自动套用这个样式,这就是标签选择器最大的价值。
2.1.3 语法格式:一个字符都不能错
标签选择器的语法格式极其简单,简单到只有一个部分,就是 HTML 的标签名,完全没有任何额外的符号,格式如下:
css
HTML标签名 {
样式属性名1: 样式属性值1;
样式属性名2: 样式属性值2;
/* 可以写无数条样式声明 */
}
这里给大家补充几个语法上的底层细节:
- 标签名必须和 HTML 里的标签名完全一致:HTML 里的标签是
<p>,CSS 里就必须写p,不能写P、paragraph、段落,否则浏览器完全认不出来; - 标签名前面、后面都不能加任何额外的符号:很多新手刚入门,会不小心写成
.p或者#p,这两个完全不是标签选择器,.p是类选择器,#p是 id 选择器,和标签选择器p的作用天差地别,一定要注意; - 大括号、冒号、分号必须是英文的 :绝对不能用中文的全角符号,比如中文的冒号
:、分号;、大括号{},浏览器是认不出来的,会直接导致样式不生效,这是新手最容易踩的低级错误,没有之一; - 每条样式声明结束后必须加分号:哪怕是最后一条声明,也推荐大家加上分号,避免后面加新的声明时,忘记加分号导致样式失效。
2.1.4 完整可运行代码示例:从基础到进阶,全覆盖
我们给大家准备了 3 个不同难度的代码示例,从最基础的用法,到实际开发中最常用的场景,全部覆盖
示例 1:最基础的标签选择器用法
这个示例是最基础的,让大家直观看到标签选择器的 "批量生效" 效果:
html
<!DOCTYPE html>
<!-- 声明这是一个HTML5文档,告诉浏览器要用HTML5的标准来解析页面 -->
<html lang="zh-CN">
<!-- html是整个页面的根标签,lang="zh-CN"告诉浏览器这个页面的语言是简体中文,避免出现乱码 -->
<head>
<!-- head标签里放的是页面的元信息,给浏览器看的,不会直接显示在页面上 -->
<meta charset="UTF-8">
<!-- 声明页面的字符编码是UTF-8,这是国际通用的字符编码,能完美支持中文,避免乱码 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 这个标签是用来适配移动端的,告诉浏览器页面的宽度要跟随设备的屏幕宽度,初始缩放比例是1.0,不会自动放大缩小 -->
<title>标签选择器基础示例</title>
<!-- 页面的标题,会显示在浏览器的标签栏上 -->
<!-- 这里是CSS代码的区域,所有的CSS样式都写在style标签里 -->
<style>
/* 标签选择器:选中页面里所有的p标签,给它们统一设置样式 */
p {
/* color属性:控制文字的颜色,这里设置为红色 */
color: red;
/* font-size属性:控制文字的大小,px是像素单位,是前端开发里最常用的尺寸单位 */
font-size: 30px;
/* line-height属性:控制文字的行高,也就是每行文字之间的间距,1.5代表行高是文字大小的1.5倍,能大幅提升阅读体验 */
line-height: 1.5;
/* margin属性:控制元素的外边距,也就是这个元素和其他元素之间的距离,20px 0代表上下外边距20像素,左右外边距0 */
margin: 20px 0;
/* font-family属性:控制文字的字体,这里设置为微软雅黑,如果用户电脑里没有微软雅黑,就用系统默认的无衬线字体 */
font-family: "微软雅黑", sans-serif;
}
/* 标签选择器:选中页面里所有的div标签,给它们统一设置样式 */
div {
/* 文字颜色设置为绿色 */
color: green;
/* 文字大小设置为24像素 */
font-size: 24px;
/* background-color属性:控制元素的背景颜色,#f5f5f5是浅灰色的十六进制颜色值,是前端开发里最常用的背景色 */
background-color: #f5f5f5;
/* padding属性:控制元素的内边距,也就是元素的边框和内容之间的距离,这里设置为上下左右都是20像素 */
padding: 20px;
/* border属性:控制元素的边框,三个值分别是边框宽度、边框样式、边框颜色,这里是1像素宽、黑色、实线边框 */
border: 1px solid #000;
/* border-radius属性:控制元素的圆角,数值越大,圆角越圆,8像素是非常常用的圆角数值 */
border-radius: 8px;
/* 外边距设置为上下20像素,左右20像素 */
margin: 20px;
}
/* 标签选择器:选中页面里所有的h1标签,给它们统一设置样式 */
h1 {
color: #333;
font-size: 36px;
/* text-align属性:控制文字的对齐方式,center代表居中对齐,还有left左对齐、right右对齐可以选 */
text-align: center;
/* font-weight属性:控制文字的粗细,bold代表加粗,normal代表正常粗细 */
font-weight: bold;
margin: 30px 0;
}
</style>
</head>
<body>
<!-- body标签里放的是页面的主体内容,所有要显示在浏览器窗口里的内容,都要写在这里 -->
<!-- 这个h1标签,会被上面的h1标签选择器选中,自动套用居中、36px、加粗的样式 -->
<h1>标签选择器效果演示</h1>
<!-- 这三个p标签,都会被上面的p标签选择器选中,自动套用红色、30px、1.5倍行高的样式,不用给每个p标签单独设置 -->
<p>咬人猫(第一个p标签,自动套用p标签的样式)</p>
<p>咬人猫(第二个p标签,同样自动套用样式,不用写任何额外代码)</p>
<p>咬人猫(第三个p标签,哪怕内容不一样,只要是p标签,都会套用样式)</p>
<!-- 这三个div标签,都会被上面的div标签选择器选中,自动套用绿色、24px、浅灰色背景、圆角边框的样式 -->
<div>阿叶君(第一个div标签,自动套用div标签的样式)</div>
<div>阿叶君(第二个div标签,同样自动套用样式)</div>
<div>阿叶君(第三个div标签,批量生效,代码极其简洁)</div>
</body>
</html>
大家把这段代码复制到 VS Code、HBuilder 等代码编辑器里,保存为.html后缀的文件,然后用浏览器打开,就能看到非常直观的效果:
- 页面里所有的
<p>标签,文字全都是红色的、30 像素大小、1.5 倍行高; - 所有的
<div>标签,文字全都是绿色的、24 像素大小、带浅灰色背景和圆角边框; - 所有的
<h1>标签,都是居中、36 像素、加粗的深灰色标题。
我们只写了一遍样式,所有同名的标签都自动套用了,完全不用给每个标签单独设置,这就是标签选择器的强大之处。
示例 2:标签选择器的实际开发用法 ------CSS 默认样式重置
这个示例是实际企业级开发里,标签选择器最常用的场景 ------CSS 默认样式重置,也是新手必须掌握的知识点。
很多新手会遇到一个问题:"我明明没给元素设置外边距,为什么页面里的元素之间有空隙?""为什么我的页面在 Chrome 里和在 IE 里显示的不一样?"
这是因为,不同的浏览器厂商,会给 HTML 标签设置一套自己的默认样式,也叫用户代理样式。比如:
- Chrome 浏览器会给
<body>标签设置默认的 8 像素外边距; - 所有浏览器都会给
<ul>、<ol>标签设置默认的 40 像素左内边距,还有上下 16 像素的外边距; - 所有浏览器都会给
<p>标签设置上下 16 像素的外边距; - 所有浏览器都会给
<h1>到<h6>标签设置默认的加粗和外边距; - IE 浏览器和 Chrome 浏览器给表单元素设置的默认样式,完全不一样。
这些默认样式,会导致两个问题:
- 页面会出现很多我们不想要的默认间距,打乱我们的排版;
- 同一个页面,在不同的浏览器里显示的效果不一样,出现兼容性问题。
所以,在实际开发中,我们都会在 CSS 的最开头,用标签选择器,把这些浏览器的默认样式清除掉,让所有标签在所有浏览器里的初始状态都一致,这个过程就叫做CSS 重置(CSS Reset)。
下面给大家一个入门级的 CSS 重置代码,全都是用标签选择器实现的,也是企业开发里最基础、最常用的重置代码:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>标签选择器:CSS默认样式重置</title>
<style>
/* 入门级CSS重置代码,全部用标签选择器实现,清除浏览器的默认样式 */
/* 清除body标签的默认外边距,解决页面四周有空白的问题 */
body {
margin: 0;
/* 给整个页面设置统一的默认字体,提升页面的一致性 */
font-family: "微软雅黑", "PingFang SC", sans-serif;
/* 给整个页面设置统一的默认文字颜色,#333是深灰色,比纯黑色更护眼,是前端开发里最常用的正文颜色 */
color: #333;
}
/* 清除p标签的默认上下外边距,避免出现多余的间距 */
p {
margin: 0;
}
/* 清除ul、ol标签的默认内边距和外边距,还有默认的列表符号 */
ul,
ol {
margin: 0;
padding: 0;
/* list-style属性:控制列表的符号,none代表去掉默认的圆点、数字符号 */
list-style: none;
}
/* 清除h1-h6标题标签的默认加粗和外边距,我们可以自己重新设置样式 */
h1, h2, h3, h4, h5, h6 {
margin: 0;
font-weight: normal;
}
/* 清除a标签的默认下划线和默认蓝色,我们可以自己重新设置样式 */
a {
/* text-decoration属性:控制文字的装饰线,none代表去掉下划线,underline是下划线,line-through是删除线 */
text-decoration: none;
color: #333;
}
/* 清除img标签的默认底部空白间隙,这是新手很容易踩的坑 */
img {
/* display: block; 把图片从行内元素变成块级元素,就能去掉底部的空白间隙 */
display: block;
}
</style>
</head>
<body>
<!-- 重置之后,这些标签都没有了浏览器的默认样式,我们可以自己完全控制它们的样式 -->
<h1>这是标题,没有默认的加粗和外边距</h1>
<p>这是段落,没有默认的上下外边距</p>
<ul>
<li>列表项1,没有默认的圆点和间距</li>
<li>列表项2,没有默认的圆点和间距</li>
</ul>
<a href="#">这是链接,没有默认的蓝色和下划线</a>
</body>
</html>
大家可以对比一下,加了重置代码和没加重置代码的页面,效果天差地别。加了重置代码之后,所有标签的默认样式都被清除了,页面的所有样式都由我们自己控制,不会出现多余的间距,也不会出现浏览器兼容性问题,这就是标签选择器在实际开发中最核心的用途之一。
2.1.5 作用特点:优点和缺点都非常鲜明
标签选择器的特点非常鲜明,优点和缺点都极其突出
优点 1:写法极其简单,零学习成本
标签选择器只需要写 HTML 的标签名,不需要加任何额外的符号,也不需要修改 HTML 代码,只要你认识 HTML 标签,就能写出标签选择器,学习成本为零,这是它最大的优点之一。
优点 2:能实现批量样式设置,代码量极少
这是标签选择器最核心的优点,也是它不可替代的原因。只要写一遍样式,就能作用于页面里所有同名的标签,不用给每个标签单独设置样式,代码量极少,完全没有冗余,效率极高。
比如页面里有 100 个<p>标签,你想让它们的文字都是 16 像素、深灰色、1.5 倍行高,用标签选择器只需要写 3 行代码,就能全部生效。如果不用标签选择器,你就得给 100 个<p>标签,一个一个设置样式,工作量直接翻了 100 倍。
缺点 1:不能差异化选择,只能 "一刀切"
这是标签选择器最大的缺点,也是它的局限性所在。标签选择器会选中页面里所有同名的标签,要么全改,要么全不改,不能只给其中的某几个标签设置特殊样式。
举个例子,页面里有 10 个<p>标签,其中 9 个你想要红色文字,剩下的 1 个你想要蓝色文字,这时候标签选择器就完全做不到了 ------ 你用p { color: red; },所有 10 个<p>都会变成红色,没法单独给那 1 个设置蓝色。
这就像学校的广播通知,只能给高一年级的所有同学发通知,不能只给高一一班的某几个同学发通知,只能 "一刀切",没法差异化处理。
缺点 2:优先级极低,很容易被其他选择器覆盖
在 CSS 的优先级体系里,标签选择器的优先级是非常低的,只比通配符选择器高一点。只要有其他选择器(比如类选择器、id 选择器)给同一个元素设置了同属性的样式,标签选择器的样式就会被直接覆盖,完全不生效。
很多新手会遇到这种情况:"我明明给 p 标签设置了红色,为什么页面里的 p 标签还是蓝色的?",大概率就是因为有其他优先级更高的选择器,给这个 p 标签设置了蓝色,覆盖了标签选择器的样式。
2.1.6 适用场景:什么时候该用标签选择器?
根据标签选择器的优缺点,我们给大家总结了它的 3 个核心适用场景,只有在这些场景里,用标签选择器才是最优解,其他场景尽量不要滥用。
场景 1:给页面里所有同名标签设置通用的、全局的基础样式
这是标签选择器最主要、最推荐的使用场景。比如:
- 给整个页面的
<body>标签设置全局的字体、文字颜色、行高; - 给所有的
<p>标签设置统一的默认字体大小、行高; - 给所有的
<a>标签设置统一的默认文字颜色、去掉下划线; - 给所有的
<img>标签设置最大宽度,避免图片溢出容器。
这些全局的、通用的基础样式,用标签选择器来写,是最合适的,代码简洁,批量生效,完全符合它的特性。
场景 2:CSS 默认样式重置
就像我们上面的示例 2 里写的,用标签选择器清除浏览器的默认样式,这是标签选择器在企业级开发里最常用的场景,没有之一。因为浏览器的默认样式,是针对所有同名标签的,用标签选择器来清除,正好匹配它的批量生效的特性。
场景 3:简单的 demo、测试页面,快速验证样式效果
如果你只是写一个简单的小 demo、测试页面,想快速看一下样式效果,不用考虑后期维护,用标签选择器是最省事的,不用修改 HTML 代码,直接写标签名就能设置样式,非常方便。
2.1.7 踩坑指南
我们给大家总结了新手用标签选择器时,最容易踩的 8 个坑,每一个都是真实开发中高频出现的错误,大家一定要避开:
-
标签名拼写错误 :比如把
p写成p1,把div写成deiv,把ul写成ui,浏览器认不出来,样式完全不生效。写的时候一定要仔细检查标签名的拼写,和 HTML 里的标签名完全一致。 -
给标签名加了多余的符号 :比如把
p写成.p或者#p,这两个完全不是标签选择器,样式自然不会生效。一定要记住,标签选择器直接写标签名,前面不要加任何符号。 -
用了中文的标点符号:比如中文的冒号、分号、大括号,浏览器完全认不出来,会导致整个样式块失效。写代码的时候,一定要把输入法切换到英文状态,避免出现这个问题。
-
忘记给样式声明加分号 :每条样式声明的结尾必须加分号,如果你忘记加,后面的所有样式声明都会失效。比如:
p { color: red /* 这里忘记加分号,下面的font-size完全不生效 */ font-size: 30px; } -
滥用标签选择器,导致样式污染 :很多新手图省事,不管什么场景都用标签选择器,比如写了
div { color: red; },结果页面里所有的 div 都变成了红色,包括你不想改的导航栏、页脚里的 div,导致全页面的样式都乱了,这就是样式污染。一定要记住,标签选择器只能用来设置全局的通用样式,不能用来设置局部的特殊样式。 -
标签名大小写混写 :虽然 CSS 不区分大小写,
P和p都能选中 p 标签,但是行业规范里必须统一用小写字母,大小写混写会导致代码可读性极差,也容易出现拼写错误。 -
用标签选择器设置局部的特殊样式 :比如你只想给页面里某一个 div 设置样式,结果用了
div标签选择器,导致全页面的 div 都被改了,这是新手最常见的错误。遇到这种需要差异化选择的场景,一定要用类选择器,绝对不要用标签选择器。 -
不理解优先级,标签选择器的样式被覆盖了,还不知道为什么 :很多新手写了
p { color: red; },结果页面里的 p 标签还是蓝色,就懵了,不知道为什么。这时候你要打开浏览器的开发者工具,看一下是不是有类选择器、id 选择器给这个 p 标签设置了蓝色,覆盖了标签选择器的样式。
2.2 类选择器:CSS 里最灵活、最常用、最重要的选择器,没有之一
2.2.1 核心定义:什么是类选择器?
类选择器,是 CSS 里最核心、最灵活、最常用的选择器,没有之一,也是我们整个 CSS 学习生涯里,用得最多的选择器。
它的核心定义是:通过我们自己给 HTML 标签定义的 class(类)属性,来选中对应的元素,实现差异化、精准化的样式设置。
如果说标签选择器是 "给整个年级发广播通知",那类选择器就是 "给加入了某个社团的同学发通知"------ 不管你是哪个年级、哪个班的,只要你加入了这个社团,就会收到通知;哪怕你是同一个班的同学,没加入这个社团,就收不到通知。
类选择器完美解决了标签选择器 "不能差异化选择" 的致命缺点,它可以让你:
- 给页面里的部分标签,设置相同的样式,不用影响其他同名标签;
- 给不同名的标签,设置相同的样式,实现样式的跨标签复用;
- 给同一个标签,同时设置多个不同的样式,实现样式的模块化组合;
- 只修改 CSS 代码,不用修改 HTML 结构,就能调整页面的样式,维护性极强。
可以说,整个 CSS 的样式复用、模块化、可维护性,都是建立在类选择器的基础上的,它是 CSS 的灵魂核心,也是我们必须 100% 吃透的选择器。
2.2.2 生活化类比:一秒搞懂类选择器的本质
我们还是用学校的例子,给大家做一个通俗易懂的生活化类比,让大家彻底理解类选择器的本质:
类选择器,就像是学校里的社团徽章。学校里有很多学生,每个学生都可以自由加入一个或多个社团,每个社团都有自己专属的徽章。学校要给某个社团的同学发通知,只需要说:"所有戴红色社团徽章的同学,来会议室开会",不管你是高一年级的,还是高二年级的,只要你戴了这个徽章,就要来开会;没戴这个徽章的,哪怕你和戴徽章的同学在同一个班,也不用来。
这个例子里的核心要素,和类选择器完全对应:
- "社团" :就是我们在 CSS 里定义的类(class) ,比如
.red、.box、.btn; - "社团徽章" :就是我们给 HTML 标签加的
class="类名"属性,相当于给标签戴上了这个社团的徽章; - "戴同一个徽章的所有同学" :就是页面里所有带同一个 class 属性的 HTML 标签 ,不管它是
<p>、<div>,还是<a>标签,只要带了同一个 class,都会被类选择器选中; - "一个同学可以加入多个社团,戴多个徽章" :就是一个 HTML 标签可以同时使用多个类名,实现多个样式的组合;
- "给戴徽章的同学发通知":就是我们给类选择器设置的样式声明,只会对带了对应 class 的标签生效,对其他标签完全没影响。
这个类比完美诠释了类选择器的核心特性:灵活、可复用、可组合、精准,这也是它为什么能成为 CSS 里最重要的选择器的原因。
2.2.3 语法格式:分为 CSS 定义和 HTML 调用两部分,一个细节都不能错
类选择器的语法分为两个部分,一个是在 CSS 里定义类,一个是在 HTML 里调用类,两者必须配合使用,缺一不可,我们给大家把每个细节都讲透。
第一部分:在 CSS 里定义类
在 CSS 里,类选择器必须以 ** 英文句号.** 开头,后面跟我们自己定义的类名,格式如下:
.自定义的类名 {
样式属性名1: 样式属性值1;
样式属性名2: 样式属性值2;
/* 可以写无数条样式声明 */
}
这里给大家补充几个定义类时的底层细节:
- 必须以英文句号
.开头,绝对不能漏 :这是类选择器的标志,浏览器看到.开头的选择器,就知道这是一个类选择器。如果漏了.,浏览器就会把它当成标签选择器,去找和类名同名的 HTML 标签,自然就找不到元素,样式不生效 - 类名是我们自己定义的,必须符合命名规范:类名不能随便乱起,必须符合我们后面会讲的命名规范,不然会出现兼容性问题、可读性问题。
- 类名区分大小写 :
.Red和.red是两个完全不同的类,浏览器会把它们当成两个不同的选择器,所以我们必须统一用小写字母,避免出现这种问题。 - 一个 CSS 文件里,可以定义无数个不同的类:没有数量限制,你可以根据需求,定义任意多个类。
第二部分:在 HTML 里调用类
在 CSS 里定义好类之后,我们需要在 HTML 标签里,用class属性来调用这个类,格式如下:
<标签名 class="你在CSS里定义的类名">标签里的内容</标签名>
这里给大家补充几个调用类时的底层细节,新手一定要记牢:
- class 属性里的类名,不需要加英文句号
.:只需要写类名本身就行,比如 CSS 里定义的是.red,HTML 里就写class="red",绝对不能写class=".red" - 一个标签可以同时调用多个类名 :多个类名之间用空格 分隔,比如
class="box red-bg border",这个标签会同时套用.box、.red-bg、.border三个类的所有样式,这是类选择器最强大的特性之一,我们后面会详细讲。 - 同一个类名,可以给无数个不同的标签调用 :不管是
<p>、<div>、<a>、<button>,还是其他任何标签,都可以调用同一个类名,实现样式的跨标签复用。 - class 属性是 HTML 里几乎所有标签都支持的原生属性 :除了
<html>、<head>、<meta>这些元信息标签,几乎所有显示在页面里的标签,都支持 class 属性,都可以调用类。
2.2.4 完整可运行代码示例:从基础到进阶,全覆盖
我们给大家准备了 4 个不同难度的代码示例,从最基础的用法,到实际开发中最常用的模块化组合用法,全部覆盖,大家可以直接复制运行,亲眼看到类选择器的强大之处。
示例 1:最基础的类选择器用法,解决标签选择器的差异化问题
这个示例是最基础的,我们用类选择器,给同一个标签名的不同元素,设置不同的样式,完美解决标签选择器 "一刀切" 的问题:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>类选择器基础用法示例</title>
<style>
/* 先用标签选择器,给所有p标签设置通用的基础样式:30px大小、1.5倍行高、上下20px外边距 */
p {
font-size: 30px;
line-height: 1.5;
margin: 20px 0;
font-family: "微软雅黑", sans-serif;
}
/* 类选择器:定义一个.red类,设置文字颜色为红色 */
.red {
color: red;
}
/* 类选择器:定义一个.blue类,设置文字颜色为蓝色 */
.blue {
color: blue;
}
/* 类选择器:定义一个.green类,设置文字颜色为绿色 */
.green {
color: green;
}
/* 类选择器:定义一个.bold类,设置文字加粗 */
.bold {
font-weight: bold;
}
</style>
</head>
<body>
<!-- 这个p标签,调用了.red类,会套用.red类的红色样式,同时套用p标签的通用基础样式 -->
<p class="red">咬人猫(红色,调用了.red类)</p>
<!-- 这个p标签,调用了.blue类,会套用.blue类的蓝色样式,同时套用通用基础样式 -->
<p class="blue">咬人猫(蓝色,调用了.blue类)</p>
<!-- 这个p标签,调用了.green类和.bold类,会同时套用两个类的样式:绿色、加粗 -->
<p class="green bold">咬人猫(绿色+加粗,同时调用了两个类)</p>
<!-- 这个p标签,没有调用任何类,只会套用p标签的通用基础样式,文字是默认的黑色 -->
<p>咬人猫(默认黑色,没有调用任何类)</p>
</body>
</html>
大家把这段代码用浏览器打开,就能直观地看到类选择器的效果:
- 同样都是
<p>标签,我们可以给它们设置不同的文字颜色,有的红、有的蓝、有的绿,完美实现了差异化选择,解决了标签选择器的致命缺点; - 同一个标签可以同时调用多个类,第三个 p 标签同时调用了
.green和.bold,同时拥有了绿色和加粗的样式,实现了样式的组合。
示例 2:类选择器的跨标签复用,同一个类给不同的标签用
这个示例给大家演示类选择器的复用性:同一个类,可以给完全不同的 HTML 标签调用,实现样式的跨标签复用,代码极其简洁。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>类选择器跨标签复用示例</title>
<style>
/* 类选择器:定义一个.red-text类,设置文字颜色为红色,这个类可以给任何标签用 */
.red-text {
color: red;
font-size: 24px;
line-height: 1.5;
margin: 20px 0;
}
</style>
</head>
<body>
<!-- p标签调用.red-text类,生效 -->
<p class="red-text">这是p标签,调用了.red-text类,文字是红色24px</p>
<!-- div标签调用.red-text类,同样生效 -->
<div class="red-text">这是div标签,同样调用了.red-text类,文字也是红色24px</div>
<!-- h3标签调用.red-text类,同样生效 -->
<h3 class="red-text">这是h3标题标签,调用了.red-text类,文字也是红色24px</h3>
<!-- a标签调用.red-text类,同样生效 -->
<a href="#" class="red-text">这是a链接标签,调用了.red-text类,文字也是红色24px,没有默认蓝色</a>
<!-- span标签调用.red-text类,同样生效 -->
<span class="red-text">这是span行内标签,调用了.red-text类,文字也是红色24px</span>
</body>
</html>
大家运行这段代码就能看到,我们只定义了一个.red-text类,不管是<p>、<div>、<h3>、<a>还是<span>标签,只要调用了这个类,都会套用对应的样式,完全不用给每个标签单独写一遍样式,复用性拉满,代码量极少。
示例 3:类选择器的核心进阶用法 ------ 多类名组合,实现样式模块化
这个示例是类选择器最核心、最强大的用法,也是企业级开发里最常用的写法 ------ 多类名组合,实现样式的模块化,把公共样式和特殊样式拆分开,代码复用性、可维护性直接拉满。
我们用前端开发里最常见的按钮场景来举例,一个网站里会有很多按钮,它们有相同的基础样式(宽、高、圆角、字体大小、边框),但是有不同的背景颜色(主按钮是蓝色,次要按钮是白色,危险按钮是红色)、不同的尺寸(大、中、小)。如果不用多类名组合,我们就要给每个按钮都写一遍完整的样式,代码冗余度极高;用了多类名组合,我们只需要把公共样式提取出来,特殊样式拆分成单独的类,自由组合就行。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>类选择器多类名组合:按钮模块化示例</title>
<style>
/* 第一步:提取所有按钮的公共基础样式,定义成.btn类,所有按钮都要调用这个类 */
.btn {
/* 去掉按钮的默认边框 */
border: none;
/* 按钮的圆角 */
border-radius: 4px;
/* 按钮的内边距,控制按钮的大小 */
padding: 8px 16px;
/* 按钮的字体大小 */
font-size: 14px;
/* 按钮的文字居中 */
text-align: center;
/* 鼠标移到按钮上,变成小手形状,提示用户可以点击 */
cursor: pointer;
/* 按钮的行高 */
line-height: 1.5;
/* 按钮的左右外边距 */
margin: 0 10px;
}
/* 第二步:拆分不同的背景颜色,定义成单独的类 */
/* 主按钮:蓝色背景,白色文字 */
.btn-primary {
background-color: #1677ff;
color: #fff;
}
/* 次要按钮:白色背景,灰色边框,黑色文字 */
.btn-default {
background-color: #fff;
border: 1px solid #d9d9d9;
color: #333;
}
/* 危险按钮:红色背景,白色文字 */
.btn-danger {
background-color: #ff4d4f;
color: #fff;
}
/* 第三步:拆分不同的尺寸,定义成单独的类 */
/* 大按钮 */
.btn-large {
padding: 12px 24px;
font-size: 16px;
}
/* 小按钮 */
.btn-small {
padding: 4px 8px;
font-size: 12px;
}
</style>
</head>
<body>
<!-- 基础主按钮:调用公共类.btn + 颜色类.btn-primary -->
<button class="btn btn-primary">主按钮</button>
<!-- 基础次要按钮:调用公共类.btn + 颜色类.btn-default -->
<button class="btn btn-default">次要按钮</button>
<!-- 基础危险按钮:调用公共类.btn + 颜色类.btn-danger -->
<button class="btn btn-danger">危险按钮</button>
<br><br>
<!-- 大尺寸主按钮:调用公共类.btn + 颜色类.btn-primary + 尺寸类.btn-large -->
<button class="btn btn-primary btn-large">大主按钮</button>
<!-- 小尺寸危险按钮:调用公共类.btn + 颜色类.btn-danger + 尺寸类.btn-small -->
<button class="btn btn-danger btn-small">小危险按钮</button>
</body>
</html>
大家运行这段代码就能看到,我们只定义了 1 个公共类、3 个颜色类、2 个尺寸类,就能组合出各种各样的按钮:主按钮、次要按钮、危险按钮、大按钮、小按钮、大危险按钮、小次要按钮等等,只要你想,就能组合出无数种不同的按钮,完全不用重复写代码。
这种写法的好处,简直是碾压级的:
- 代码复用性拉满:公共样式只写一遍,所有按钮都能复用,没有任何冗余代码;
- 维护成本极低 :如果后期要修改所有按钮的圆角,只需要修改
.btn类里的border-radius这一行代码,所有按钮都会自动更新,不用一个一个修改;如果要修改主按钮的蓝色,只需要修改.btn-primary里的背景色,所有主按钮都会更新,效率极高; - 灵活性极高:我们可以根据需求,自由组合不同的类,想要什么类型的按钮,就组合对应的类,不用修改 CSS 代码,只需要调整 HTML 里的 class 属性就行;
- 可读性极强 :看到 HTML 里的
class="btn btn-primary btn-large",哪怕是刚接手项目的开发者,也能一眼看出来,这是一个大尺寸的主按钮,代码含义极其清晰。
这就是类选择器多类名组合的强大之处,也是现在企业级前端开发里,最核心、最主流的 CSS 写法,甚至衍生出了很多 CSS 架构规范,比如 BEM 规范,都是基于类选择器的这个特性来的。
2.2.5 类名命名规范:新手必须遵守的行业通用规则
类名是我们自己定义的,但是绝对不能随便乱起,一个好的类名,能让你的代码可读性、可维护性提升 10 倍;一个乱起的类名,会让你的代码变成 "天书",哪怕是你自己,过一个月也看不懂自己写的是什么。
行业里有一套通用的类名命名规范,新手从一开始就要养成良好的命名习惯,我们给大家总结了 8 条核心规则,每条都有正确和错误的示例,大家一定要严格遵守:
规则 1:必须用有意义的英文单词,绝对不能用中文、纯数字、无意义的字母
- 正确示例 :
.header(头部)、.nav(导航)、.banner(轮播图)、.content(内容)、.footer(页脚)、.btn(按钮)、.title(标题)、.list(列表); - 错误示例 :
- 中文类名:
.头部、.红色、.按钮(部分老浏览器不支持中文,会出现乱码,样式不生效,而且国际化项目完全无法维护); - 纯数字类名:
.1、.2、.123(CSS 规范里,类名不能以数字开头,浏览器会解析失败,样式不生效); - 无意义的字母:
.a、.b、.aaa、.x(过一个月你自己都忘了这个类是干嘛的,其他开发者更是完全看不懂,维护性为 0); - 拼音类名:
.hongse、.anniu、.daohang(不是所有人都能看懂拼音,尤其是国际化团队,而且拼音的可读性极差,不如英文)。
- 中文类名:
规则 2:多个单词组成的类名,必须用连字符-分隔,绝对不能用下划线_、驼峰命名法
- 正确示例 :
.main-content(主要内容)、.nav-bar(导航栏)、.product-list(商品列表)、.user-avatar(用户头像)、.btn-primary(主按钮); - 错误示例 :
- 下划线分隔:
.main_content、.nav_bar(虽然语法上允许,但是不符合 CSS 的行业通用规范,CSS 里的官方属性都是用连字符分隔的,比如font-size、background-color,类名要和官方规范保持一致); - 驼峰命名法:
.mainContent、.navBar、.btnPrimary(驼峰命名法是 JavaScript 里的命名规范,CSS 里不推荐使用,会导致代码风格混乱,而且 CSS 本身不区分大小写,容易出现拼写错误); - 大驼峰命名法:
.MainContent、.NavBar(这是 JavaScript 里类的命名规范,CSS 里绝对不能用)。
- 下划线分隔:
用连字符-分隔多个单词,是 CSS 行业里的通用铁律,所有的前端团队都会遵守这个规范,新手一定要从一开始就养成习惯。
规则 3:类名必须以英文字母开头,绝对不能以数字、符号开头
- 正确示例 :
.box1、.item2、.title3; - 错误示例 :
.1box、.2item、.3title、.-box、._box。
这是 CSS 的官方语法规范,类名如果以数字、符号开头,浏览器会直接解析失败,类选择器完全不生效,一定要注意。
规则 4:类名要尽量简洁,同时要能表达清楚含义,在简洁和表意之间找平衡
- 正确示例 :
.nav(导航,全称是 navigation,简洁又表意清晰)、.btn(按钮,全称是 button)、.list(列表)、.banner(轮播图); - 错误示例 :
- 太长的类名:
.this-is-a-very-long-class-name-for-the-main-content-of-the-home-page(完全没必要,太长了,写起来麻烦,可读性也差); - 太短的类名:
.n、.b、.l(太简洁了,没人知道是什么意思,可读性为 0)。
- 太长的类名:
规则 5:绝对不要用 HTML 的标签名作为类名
- 正确示例 :
.title-text、.content-box、.nav-list; - 错误示例 :
.p、.div、.h1、.ul、.a。
虽然语法上允许这么做,但是这会导致代码的可读性极差,其他开发者看到.p,会下意识以为是标签选择器,结果是类选择器,非常容易混淆,而且完全没有表意性,不知道这个类是干嘛的,一定要绝对避免。
规则 6:类名要语义化,根据元素的用途命名,而不是根据样式命名
这是非常重要的一条进阶规则,也是区分新手和专业开发者的关键。
- 正确示例(根据用途命名) :
.btn-primary(主按钮)、.btn-danger(危险按钮)、.nav(导航)、.user-info(用户信息)、.price(价格)、.title(标题); - 错误示例(根据样式命名) :
.red、.blue、.font-24、.mt-20、.bg-green。
很多新手喜欢根据样式命名,比如写一个.red { color: red; },看起来很方便,但是后期维护的时候,会出现致命的问题:比如产品经理说,这个按钮的颜色要从红色改成蓝色,你就得做两件事:1. 修改 CSS 里的.red类,把颜色改成蓝色;2. 把 HTML 里所有的class="red"改成class="blue",不然代码里的类名是.red,实际样式是蓝色,会变得极其离谱。
而如果我们根据用途命名,比如.btn-danger,哪怕后期把颜色从红色改成橙色,我们只需要修改 CSS 里的.btn-danger的背景色就行,HTML 里的代码完全不用动,类名的语义还是对的,这就是语义化命名的核心价值:让类名和样式解耦,只关注元素的用途,不关注它长什么样,后期维护成本极低。
当然,这条规则不是绝对的,如果你写的是通用的工具类,比如.clearfix(清除浮动)、.text-center(文字居中)、.hidden(隐藏元素),这些是可以根据样式命名的,因为它们的用途就是这个固定的样式,不会变。但是对于业务相关的类名,一定要根据用途命名,绝对不要根据样式命名。
规则 7:同一个项目里,命名风格必须统一
比如你用了连字符分隔,就整个项目都用连字符,不要有的用连字符,有的用下划线,有的用驼峰,会导致代码风格极其混乱,可读性极差。
规则 8:不要用浏览器的保留字、关键字作为类名
比如.window、.document、.navigator、.console这些,都是浏览器的内置对象名,用它们作为类名,可能会导致后续 JavaScript 开发出现冲突和 bug,一定要避免。
2.2.6 作用特点:几乎全是优点,缺点可以忽略不计
类选择器的特点,几乎全是优点,缺点只有微不足道的一点点,这也是它为什么能成为 CSS 里最重要的选择器的原因:
优点 1:完美实现差异化选择,精准控制元素
类选择器可以只选中页面里你想要的那几个元素,不会影响其他同名的元素,完美解决了标签选择器 "一刀切" 的致命缺点,想改哪个元素,就给哪个元素加对应的 class,精准度极高。
优点 2:复用性极强,跨标签、跨页面复用
同一个类,可以给页面里无数个不同的标签调用,也可以给网站里所有的页面调用,样式只需要写一遍,就能到处用,代码冗余度极低,复用性拉满。
优点 3:支持多类名组合,实现样式模块化
这是类选择器最强大的特性,没有之一。我们可以把样式拆分成一个个独立的模块,每个模块对应一个类,然后根据需求自由组合,代码极其简洁、灵活、易维护,这也是现代 CSS 工程化的核心基础。
优点 4:优先级适中,不会轻易被覆盖,也不会轻易污染其他样式
在 CSS 的优先级体系里,类选择器的优先级比标签选择器高,比 id 选择器低,正好处于一个非常合适的位置:既能覆盖标签选择器的通用样式,又不会因为优先级太高,导致后期无法修改,完美平衡了灵活性和可维护性。
优点 5:只需要修改 CSS 代码,就能调整页面样式,不用修改 HTML 结构
如果后期要修改页面的样式,我们只需要修改 CSS 里对应的类的样式,HTML 里的代码完全不用动,实现了样式和结构的彻底分离,符合 CSS 的核心设计理念,维护成本极低。
优点 6:对 SEO 友好,不影响页面的语义化
类名只是用来设置样式的,不会影响 HTML 标签本身的语义,搜索引擎还是能正常识别页面的结构和内容,对 SEO 完全没有负面影响。
缺点:需要在 HTML 标签里加 class 属性,比标签选择器多写一点点代码
这是类选择器唯一的缺点,但是这一点点代码,换来的是极高的灵活性、复用性和可维护性,完全值得,根本算不上真正的缺点。
2.2.7 适用场景:几乎所有的样式设置,都优先用类选择器
类选择器的适用场景极其广泛,在企业级开发里,90% 以上的 CSS 样式,都是用类选择器来写的,我们给大家总结了它的核心适用场景:
- 给页面里的部分元素设置差异化的样式:这是最基础的场景,比如给部分段落设置特殊的颜色、给部分按钮设置特殊的样式。
- 提取公共样式,实现代码复用:把页面里重复出现的样式,提取成一个公共的类,所有需要这个样式的元素,都调用这个类,不用重复写代码。
- 样式模块化组合,实现复杂的样式需求:把不同的样式拆分成独立的类,自由组合,实现各种复杂的样式效果,比如按钮、卡片、导航栏等组件的样式。
- 企业级项目的核心样式写法:所有的企业级前端项目,都是以类选择器为核心来写 CSS 的,不管是原生 CSS,还是 Vue、React 的组件样式,都是优先用类选择器。
简单来说:只要你需要设置 CSS 样式,优先考虑用类选择器,它能解决 99% 的场景问题。
2.2.8 踩坑指南
我们给大家总结了最容易踩的 10 个坑,每一个都是高频出现的错误,大家一定要避开:
- CSS 里的类选择器漏写了开头的英文句号
.:比如把.red写成了red,浏览器会把它当成标签选择器,去找<red>标签,自然找不到元素,样式完全不生效。这是新手用类选择器时,最容易犯的错误,没有之一,写的时候一定要检查开头有没有.。 - HTML 里的 class 属性里,给类名加了英文句号
.:比如把class="red"写成了class=".red",浏览器会找类名是.red的类,而你 CSS 里定义的是.red,对应的类名是red,自然匹配不上,样式不生效。一定要记住:CSS 里定义类要加.,HTML 里调用类不要加.。 - 一个标签调用多个类名时,类名之间没有用空格分隔,用了逗号或者其他符号 :比如把
class="box red-bg"写成了class="box,red-bg"或者class="box.red-bg",浏览器会把box,red-bg当成一个完整的类名,自然找不到对应的类,样式不生效。多个类名之间,必须用空格分隔,绝对不能用其他符号。 - 类名拼写错误 :CSS 里定义的是
.primary,HTML 里写的是class="primay",少了一个 r,自然匹配不上,样式不生效。写的时候一定要仔细检查类名的拼写,确保 CSS 和 HTML 里的类名完全一致。 - 类名大小写混写,CSS 和 HTML 里的大小写不一致 :CSS 里定义的是
.Red,HTML 里写的是class="red",类名是区分大小写的,这两个是完全不同的类,自然匹配不上。一定要统一用小写字母写类名,避免出现这个问题。 - 多个类的样式冲突了,以为是 HTML 里的类名顺序决定的 :很多新手会问,"我给一个标签同时加了
.red和.blue两个类,都设置了 color 属性,为什么最终是蓝色,不是红色?是不是因为 HTML 里class="red blue",blue 写在后面?"。这里给大家明确:多个类名的样式冲突时,最终生效的样式,和 HTML 里的类名顺序完全无关,只和 CSS 里的书写顺序有关。CSS 里写在后面的类,会覆盖前面的类的同属性样式,和 HTML 里的顺序没有任何关系。 - 类名命名不规范,用中文、拼音、纯数字、无意义的字母:这个我们前面已经讲过了,不规范的类名,会导致代码可读性极差,维护成本极高,甚至会出现样式不生效的问题,一定要遵守命名规范。
- 根据样式命名类名,导致后期维护灾难 :比如定义了
.red类,后期要改成蓝色,代码里的类名和实际样式完全不符,维护起来极其麻烦,一定要根据元素的用途命名,不要根据样式命名。 - 滥用类选择器,给每个元素都加一堆无意义的类:很多新手学了类选择器之后,不管什么元素都加类,哪怕是页面里唯一的一个元素,也要加个类,甚至给每个元素都加十几个类,导致 HTML 代码极其臃肿,可读性极差。类选择器要合理使用,公共样式提取成类,特殊样式加类,不要滥用。
- 不理解优先级,类选择器的样式被 id 选择器、行内样式覆盖了,还不知道为什么:类选择器的优先级比 id 选择器、行内样式低,如果你给元素加了 id 选择器,或者写了行内样式,设置了同属性的样式,类选择器的样式会被直接覆盖。遇到样式不生效的情况,一定要打开浏览器的开发者工具,看一下是不是有更高优先级的样式覆盖了。
2.3 id 选择器:给元素一个唯一的 "身份证号"
2.3.1 核心定义:什么是 id 选择器?
id 选择器,是 CSS 里唯一性最强的选择器,它的核心定义是:通过我们给 HTML 标签定义的 id 属性,来选中对应的元素,同一个 HTML 页面里,同一个 id 只能给一个元素使用,绝对不能重复。
如果说类选择器是 "可以重复的姓名",那 id 选择器就是 "唯一的身份证号"------ 每个人的身份证号都是唯一的,全国找不到第二个和你身份证号一样的人;同理,同一个 HTML 页面里,同一个 id 只能对应一个元素,找不到第二个带这个 id 的元素。
id 选择器的语法和类选择器很像,但是核心区别就是唯一性,也正是因为这个唯一性,它的使用场景和类选择器完全不同。
2.3.2 生活化类比:一秒搞懂 id 选择器的本质
我们还是用生活化的类比,让大家彻底理解 id 选择器的本质:
id 选择器,就像是我们的居民身份证号。每个中国公民都有一个唯一的身份证号,这个身份证号只属于你一个人,不会有第二个人和你重复。警察要找你,只需要报你的身份证号,就能精准找到你一个人,不会找到其他人;哪怕你和别人同名同姓,只要身份证号不一样,就能精准区分开。
这个例子里的核心要素,和 id 选择器完全对应:
- "身份证号" :就是我们在 CSS 里定义的id 选择器 ,比如
#ha、#header、#logo; - "身份证号唯一,不能重复" :就是同一个 HTML 页面里,同一个 id 只能给一个元素使用,绝对不能重复;
- "通过身份证号精准找到你一个人":就是 id 选择器只会选中页面里带对应 id 的那一个唯一的元素,不会选中其他元素;
- "姓名可以重复,身份证号不能重复" :就是类名可以重复给多个元素用,id 名绝对不能重复。
2.3.3 语法格式:分为 CSS 定义和 HTML 调用两部分,核心是唯一性
id 选择器的语法也分为两个部分,CSS 里的定义和 HTML 里的调用,和类选择器很像,但是有几个核心的区别,我们给大家讲透每个细节。
第一部分:在 CSS 里定义 id 选择器
在 CSS 里,id 选择器必须以 ** 井号#** 开头,后面跟我们自己定义的 id 名,格式如下:
#自定义的id名 {
样式属性名1: 样式属性值1;
样式属性名2: 样式属性值2;
/* 可以写无数条样式声明 */
}
这里的核心细节:
- 必须以井号
#开头,绝对不能漏 :这是 id 选择器的标志,浏览器看到#开头的选择器,就知道这是一个 id 选择器。如果漏了#,浏览器会把它当成标签选择器,样式不生效。 - id 名的命名规范,和类名完全一致 :必须用有意义的英文单词,多个单词用连字符
-分隔,以英文字母开头,不能用中文、纯数字、保留字,统一用小写字母,区分大小写。 - id 名是唯一的,一个 CSS 文件里,同一个 id 名只能定义一次:虽然语法上允许你定义多次,但是根据唯一性原则,不推荐这么做。
第二部分:在 HTML 里调用 id 选择器
在 CSS 里定义好 id 之后,我们需要在 HTML 标签里,用id属性来调用这个 id,格式如下:
<标签名 id="你在CSS里定义的id名">标签里的内容</标签名>
这里的核心细节,也是 id 选择器最重要的规则,没有之一:
- id 属性里的 id 名,不需要加井号
#:只需要写 id 名本身就行,比如 CSS 里定义的是#ha,HTML 里就写id="ha",绝对不能写id="#ha",这是新手容易踩的坑。 - 同一个 HTML 页面里,同一个 id 名只能给一个标签使用,绝对不能重复! :这是 id 选择器的铁律,是 HTML 官方规范里明确规定的,没有任何例外。比如你给第一个 div 加了
id="box",页面里其他所有的标签,都不能再用id="box"了,哪怕是不同名的标签也不行。 - 一个标签只能有一个 id 属性,只能写一个 id 名,绝对不能同时写多个 id :比如
id="box header"是完全错误的,一个标签只能有一个唯一的 id,不能同时有多个 id,这和类选择器完全不同,一定要注意。 - id 属性是 HTML 里几乎所有标签都支持的原生属性,和 class 属性一样,几乎所有显示在页面里的标签都支持 id 属性。
2.3.4 完整可运行代码示例:基础用法和唯一性演示
我们给大家准备了两个代码示例,一个是基础用法示例,一个是唯一性演示,让大家直观看到 id 选择器的效果和核心规则。
示例 1:id 选择器的基础用法示例
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>id选择器基础用法示例</title>
<style>
/* 标签选择器:给所有p标签设置通用基础样式 */
p {
font-size: 30px;
line-height: 1.5;
margin: 20px 0;
font-family: "微软雅黑", sans-serif;
}
/* id选择器:定义#ha,设置文字颜色为红色 */
#ha {
color: red;
}
/* id选择器:定义#hei,设置文字颜色为黑色,字体加粗 */
#hei {
color: black;
font-weight: bold;
}
/* id选择器:定义#xi,设置文字颜色为蓝色,背景色为浅灰色 */
#xi {
color: blue;
background-color: #f5f5f5;
padding: 10px;
border-radius: 4px;
}
</style>
</head>
<body>
<!-- 这个p标签,调用了id="ha",会套用#ha的样式,红色文字 -->
<p id="ha">蛤蛤蛤(红色,id="ha")</p>
<!-- 这个p标签,调用了id="hei",会套用#hei的样式,黑色加粗 -->
<p id="hei">嘿嘿嘿(黑色加粗,id="hei")</p>
<!-- 这个p标签,调用了id="xi",会套用#xi的样式,蓝色文字、浅灰色背景 -->
<p id="xi">嘻嘻嘻(蓝色+浅灰色背景,id="xi")</p>
<!-- 这个p标签,没有调用任何id,只会套用p标签的通用样式 -->
<p>呵呵呵(默认黑色,没有id)</p>
</body>
</html>
大家运行这段代码就能看到,每个带 id 的 p 标签,都套用了对应的 id 选择器的样式,而且每个 id 只给一个标签用,完全符合唯一性规则。
示例 2:id 选择器的唯一性演示,新手绝对不要这么写
这个示例给大家演示,id 重复使用会出现什么问题,新手绝对不要在实际开发里这么写,只是给大家看效果。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>id选择器唯一性演示:错误写法</title>
<style>
/* id选择器:定义#box,设置文字颜色为红色,30px大小 */
#box {
color: red;
font-size: 30px;
line-height: 1.5;
margin: 20px 0;
}
</style>
</head>
<body>
<!-- 第一个div,用了id="box" -->
<div id="box">第一个div,id="box"</div>
<!-- 第二个div,也用了id="box",这是完全错误的,违反了HTML规范! -->
<div id="box">第二个div,重复用了id="box",错误写法!</div>
<!-- 第三个p标签,也用了id="box",更是错上加错! -->
<p id="box">第三个p标签,也用了id="box",严重错误!</p>
</body>
</html>
大家运行这段代码会发现,虽然这三个标签都重复用了id="box",但是 CSS 样式居然都生效了,三个标签的文字都是红色 30px。很多新手会问:"你不是说 id 不能重复吗?为什么样式还能生效?"
这里给大家明确解释:CSS 里,id 重复使用,样式确实会生效,因为浏览器的 CSS 解析器,对 id 重复做了兼容处理。但是,这绝对不代表你可以这么做,因为:
- 这是完全违反 HTML 官方规范的,W3C 的 HTML 标准里,明确规定了 id 属性在整个文档中必须是唯一的,不符合规范的代码,就是不专业的、错误的代码。
- JavaScript 里,id 重复会导致致命的 bug :JavaScript 里有一个原生方法
document.getElementById(),就是通过 id 来获取元素的,这个方法只会返回页面里第一个带这个 id 的元素,后面重复的 id 的元素,完全获取不到。比如上面的示例里,你用document.getElementById("box"),只会拿到第一个 div,拿不到第二个 div 和 p 标签,后续的所有交互操作都会失效,这是致命的 bug。 - 锚点跳转功能会失效 :HTML 里可以通过 id 实现页面内的锚点跳转,比如
<a href="#box">跳转到box</a>,如果页面里有多个id="box",点击这个链接,只会跳到第一个带id="box"的元素,后面的都跳不到。 - SEO 会受到影响:搜索引擎的爬虫,会根据 id 来识别页面的结构,重复的 id 会导致爬虫无法正确解析页面,影响页面的搜索排名。
- 无障碍访问会失效:屏幕阅读器等无障碍工具,是通过 id 来关联页面元素的,重复的 id 会导致这些工具完全无法正常工作,影响残障用户的访问。
所以,哪怕 CSS 样式能生效,同一个 HTML 页面里,id 也绝对不能重复使用,这是前端开发的铁律,没有任何例外,新手从一开始就要养成习惯,绝对不要写重复的 id。
2.3.5 id 选择器和类选择器的核心区别对比表
为了让大家更清晰、更直观地记住 id 选择器和类选择器的区别,我们给大家做了一个超详细的对比表,把所有核心维度都覆盖了:
| 对比维度 | 类选择器 | id 选择器 |
|---|---|---|
| CSS 里的标识符号 | 英文句号.开头,比如.red |
井号#开头,比如#red |
| HTML 里的属性 | class="类名" |
id="id名" |
| 唯一性规则 | 不唯一,同一个类名可以给页面里无数个元素使用 | 绝对唯一,同一个 HTML 页面里,同一个 id 名只能给一个元素使用,绝对不能重复 |
| 单个元素的使用数量 | 一个元素可以同时使用多个类名,多个类名用空格分隔 | 一个元素只能有一个 id,只能写一个 id 名,绝对不能同时用多个 id |
| 优先级 | 中等,比标签选择器高,比 id 选择器低 | 高,比类选择器、标签选择器都高,仅次于行内样式和!important |
| JS 获取元素的方式 | document.getElementsByClassName(),返回所有匹配的元素集合 |
document.getElementById(),只返回唯一的一个元素 |
| 主要用途 | 给元素设置 CSS 样式,是 CSS 样式的核心选择器 | 主要用于 JavaScript 获取元素、页面锚点跳转,不推荐用来设置 CSS 样式 |
| 复用性 | 复用性极强,跨标签、跨页面复用 | 复用性为 0,只能给一个元素用 |
| 灵活性 | 极高,支持多类名组合,模块化样式 | 极低,只能给一个元素用,无法组合 |
| 生活化类比 | 姓名,可以重复 | 身份证号,唯一,不能重复 |
2.3.6 作用特点:优点和缺点都极其极端
id 选择器的特点非常极端,优点和缺点都非常突出,这也是为什么现在企业级开发里,不推荐用 id 选择器来设置 CSS 样式的原因。
优点 1:唯一性极强,可以 100% 精准定位到页面里的某一个唯一的元素
因为 id 在页面里是唯一的,所以 id 选择器可以精准定位到那一个唯一的元素,不会影响其他任何元素,精准度是所有选择器里最高的。
优点 2:优先级极高,不容易被其他样式覆盖
在 CSS 的优先级体系里,id 选择器的优先级非常高,仅次于行内样式和!important,只要你用 id 选择器设置了样式,基本不会被类选择器、标签选择器的样式覆盖,稳定性极强。
优点 3:JavaScript 获取元素的效率最高
通过document.getElementById()获取元素,是浏览器里效率最高、速度最快的元素获取方式,比通过类名、标签名获取要快得多,这也是 id 选择器最核心的价值。
缺点 1:唯一性太强,复用性为 0,完全无法复用样式
这是 id 选择器最大的缺点,也是它不适合用来设置 CSS 样式的核心原因。同一个 id 只能给一个元素用,你写的 id 选择器的样式,只能给这一个元素用,无法给其他元素复用,代码冗余度极高,完全不符合 CSS 样式复用的设计理念。
比如你有 10 个按钮,都要用同样的样式,用类选择器只需要写一遍.btn,所有按钮都能调用;用 id 选择器的话,你要给 10 个按钮分别定义 10 个不同的 id,写 10 遍完全一样的样式,代码量直接翻了 10 倍,全是冗余代码。
缺点 2:优先级太高,导致后期样式维护极其困难
id 选择器的优先级太高了,如果你用 id 选择器设置了样式,后期想要修改这个样式,只能用另一个 id 选择器,或者用行内样式,或者用!important,否则根本覆盖不了。
在大型项目里,到处都是 id 选择器的样式,会导致样式覆盖变得极其混乱,最后只能到处加!important,代码变得完全无法维护,这是企业级开发里的大忌。
缺点 3:一个元素只能用一个 id,无法实现样式的模块化组合
类选择器可以给一个元素加多个类,实现样式的自由组合;而 id 选择器,一个元素只能有一个 id,完全无法组合样式,灵活性极差。
2.3.7 适用场景:主要用于 JavaScript,不推荐用来设置 CSS 样式
根据 id 选择器的特点,我们给大家明确了它的核心适用场景,以及不推荐的场景:
推荐的适用场景
- JavaScript 获取元素,实现页面交互效果 :这是 id 选择器现在最主要、最核心的用途,没有之一。比如你要给一个按钮加点击事件,给一个弹窗加显示隐藏的效果,给一个表单加验证逻辑,都可以给这个元素加一个唯一的 id,然后用
document.getElementById()获取到这个元素,再写交互逻辑。 - 页面内的锚点跳转 :比如页面顶部的导航栏,点击 "联系我们",直接跳转到页面底部的联系我们板块,就可以给底部的板块加一个
id="contact",然后导航栏的链接写<a href="#contact">联系我们</a>,点击就能直接跳转到对应的位置,这是 HTML 里非常常用的锚点跳转功能。 - 给页面里唯一的、不会复用的元素设置样式:比如页面里唯一的页面根容器、唯一的头部、唯一的页脚,这些元素在页面里只会出现一次,不会复用,也可以用 id 选择器来设置样式,但是更推荐用类选择器。
绝对不推荐的场景
- 给需要复用的元素设置样式:比如按钮、卡片、列表项这些会重复出现的元素,绝对不要用 id 选择器,必须用类选择器。
- 大型项目里的通用样式:大型项目里,绝对不要到处用 id 选择器设置样式,会导致优先级混乱,维护成本极高,必须用类选择器。
这里给新手一个明确的行业通用规范:CSS 样式优先用类选择器来写,id 选择器主要留给 JavaScript 用,尽量不要用 id 选择器来设置 CSS 样式。这是现在所有前端团队都遵守的通用规范,新手从一开始就要养成这个习惯。
2.3.8 踩坑指南
- 同一个 HTML 页面里,重复使用同一个 id:这是最严重、最致命的错误,哪怕 CSS 样式能生效,也绝对不能这么做,会导致 JavaScript、锚点跳转、SEO、无障碍访问都出现问题,一定要严格遵守 id 的唯一性规则。
- CSS 里的 id 选择器漏写了开头的井号
#:比如把#ha写成了ha,浏览器会把它当成标签选择器,样式不生效,一定要检查开头的#。 - HTML 里的 id 属性里,给 id 名加了井号
#:比如把id="ha"写成了id="#ha",匹配不上 CSS 里的 id 选择器,样式不生效,一定要记住:CSS 里定义 id 要加#,HTML 里调用 id 不要加#。 - 一个标签同时写了多个 id :比如
id="box header",这是完全错误的,一个标签只能有一个 id,不能同时写多个,和类选择器完全不同,一定要注意。 - 滥用 id 选择器来设置 CSS 样式:不管什么元素都加 id,用 id 选择器来写样式,导致代码冗余、优先级混乱、无法维护,一定要记住,CSS 样式优先用类选择器,id 主要留给 JavaScript 用。
- id 名拼写错误、大小写不一致:和类选择器一样,id 名也是区分大小写的,CSS 里的 id 名和 HTML 里的 id 名必须完全一致,不然匹配不上,样式不生效。
- id 名命名不规范:用中文、拼音、纯数字、无意义的字母,和类名的命名规范一样,id 名也要用有意义的英文单词,遵守命名规范。
2.4 通配符选择器:最 "霸道" 的选择器,选中页面里的所有元素
2.4.1 核心定义:什么是通配符选择器?
通配符选择器,是 CSS 里最简单、最 "霸道" 的选择器,它的核心定义是:用星号*作为选择器,会选中当前 HTML 页面里的所有 HTML 元素,没有任何例外。
这里的 "所有元素",真的是所有:<html>、<body>、<div>、<p>、<h1>到<h6>、<ul>、<ol>、<li>、<a>、<img>、<span>、<input>、<button>,甚至是<head>里的标签(虽然它们不显示在页面上),都会被通配符选择器选中。
2.4.2 生活化类比:一秒搞懂通配符选择器的本质
通配符选择器,就像是全市的广播通知,市政府通过广播说:"全市所有市民,明天都要做核酸检测"。这个通知会发给全市的每一个市民,不管你是哪个区的、哪个街道的、哪个小区的,不管你是老人、小孩、年轻人,都会收到这个通知,没有任何例外。
这个例子里:
- "全市所有市民" :就是页面里的所有 HTML 元素;
- "全市广播通知" :就是通配符选择器
*; - "没有任何例外":就是通配符选择器会选中所有元素,不会漏掉任何一个。
2.4.3 语法格式:简单到只有一个字符
通配符选择器的语法极其简单,简单到只有一个星号*,没有任何其他的符号,格式如下:
* {
样式属性名1: 样式属性值1;
样式属性名2: 样式属性值2;
/* 可以写无数条样式声明 */
}
没有任何额外的规则,只要你写了*,就会选中页面里的所有元素,给它们统一设置样式。
2.4.4 完整可运行代码示例:基础用法和最常用的场景
我们给大家准备了两个代码示例,一个是基础用法示例,一个是实际开发中最常用的 CSS 重置示例,让大家直观看到通配符选择器的效果。
示例 1:通配符选择器基础用法示例
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>通配符选择器基础用法示例</title>
<style>
/* 通配符选择器:选中页面里的所有元素,给它们统一设置样式 */
* {
/* 所有元素的文字颜色都变成红色 */
color: red;
/* 所有元素的文字大小都变成30px */
font-size: 30px;
/* 所有元素的行高都是1.5倍 */
line-height: 1.5;
/* 清除所有元素的默认外边距 */
margin: 0;
/* 清除所有元素的默认内边距 */
padding: 0;
}
</style>
</head>
<body>
<!-- 所有的标签,都会被通配符选择器选中,文字都是红色30px -->
<p>这是p标签,红色30px</p>
<div>这是div标签,红色30px</div>
<h1>这是h1标题,红色30px</h1>
<ul>
<li>这是ul里的li,红色30px</li>
<li>这是ul里的li,红色30px</li>
</ul>
<a href="#">这是a链接,红色30px,没有默认蓝色</a>
<span>这是span标签,红色30px</span>
<button>这是按钮,红色30px</button>
</body>
</html>
大家运行这段代码就能看到,页面里所有的元素,不管是什么标签,文字都是红色的、30 像素大小,所有的默认外边距、内边距都被清除了,完美体现了通配符选择器的 "霸道"------ 选中所有元素,无一例外。
示例 2:通配符选择器最常用的场景 ------CSS 全局重置
这是通配符选择器在实际开发中,唯一推荐的使用场景,也是我们前面讲标签选择器时提到的 CSS 重置,用通配符选择器可以更简单、更彻底地清除所有元素的默认外边距和内边距。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>通配符选择器:CSS全局重置</title>
<style>
/* 通配符选择器:企业级开发里最常用的全局重置代码 */
* {
/* 清除所有元素的默认外边距 */
margin: 0;
/* 清除所有元素的默认内边距 */
padding: 0;
/* 统一盒子模型的计算方式,这是前端开发里的标准写法,入门阶段先记住,后面讲盒子模型会详细讲 */
box-sizing: border-box;
}
/* 给body设置全局的基础样式 */
body {
font-family: "微软雅黑", "PingFang SC", sans-serif;
color: #333;
font-size: 14px;
line-height: 1.5;
}
/* 清除a标签的默认样式 */
a {
text-decoration: none;
color: #333;
}
/* 清除ul、ol的默认列表符号 */
ul, ol {
list-style: none;
}
/* 清除img的默认底部空白 */
img {
display: block;
}
</style>
</head>
<body>
<!-- 所有元素的默认外边距、内边距都被清除了,我们可以完全控制页面的排版 -->
<h1>页面标题</h1>
<p>页面段落内容</p>
<ul>
<li>列表项1</li>
<li>列表项2</li>
</ul>
</body>
</html>
这段代码,是现在企业级开发里,最基础、最常用的 CSS 全局重置代码,用通配符选择器一次性清除所有元素的默认外边距和内边距,简单、高效、彻底,不用一个一个标签去清除,这也是通配符选择器唯一被广泛使用的场景。
2.4.5 作用特点:优点只有一个,缺点却很多
通配符选择器的特点非常鲜明,优点只有一个,但是缺点却不少,所以它的使用场景非常单一,绝对不能滥用。
优点:可以一次性选中页面里的所有元素,批量设置样式,代码极其简洁
这是通配符选择器唯一的优点,比如清除所有元素的默认外边距和内边距,用通配符选择器只需要写 2 行代码,就能一次性搞定,不用给每个标签都写一遍,代码极其简洁。
缺点 1:优先级极低,很容易被其他任何选择器覆盖
在 CSS 的优先级体系里,通配符选择器的优先级是最低的,比标签选择器还要低。任何其他选择器(标签选择器、类选择器、id 选择器),只要给元素设置了同属性的样式,都会直接覆盖通配符选择器的样式。
比如你用* { color: red; }给所有元素设置了红色文字,然后用p { color: blue; }给 p 标签设置了蓝色文字,最终 p 标签的文字会是蓝色,因为标签选择器的优先级比通配符选择器高。
缺点 2:会选中页面里的所有元素,包括不需要的元素,可能会影响页面的渲染性能
通配符选择器会让浏览器遍历页面里的所有 DOM 元素,给每一个元素都应用样式,哪怕这个元素是不显示在页面上的,比如<head>、<meta>、<script>这些标签。
在页面元素非常多的大型网站里,通配符选择器会给浏览器增加很多不必要的解析负担,可能会稍微影响页面的渲染性能。虽然现在的浏览器性能都很强,这个影响已经微乎其微了,但是我们还是要尽量避免不必要的性能消耗。
缺点 3:会给很多不需要的元素设置样式,导致意外的样式问题
通配符选择器会给所有元素设置样式,包括很多你意想不到的元素,比如表单元素<input>、<button>、<select>,这些元素有很多默认的浏览器样式,用通配符选择器给它们设置样式,可能会导致它们的默认样式被破坏,出现意外的显示问题。
比如你用* { font-size: 14px; }给所有元素设置了 14px 的字体大小,会导致<input>、<button>这些表单元素的默认字体大小被覆盖,在不同的浏览器里出现兼容性问题。
缺点 4:继承的样式优先级比通配符选择器还低,新手容易搞混
很多新手会搞混通配符选择器和样式继承的优先级,比如你给body设置了color: red;,通过继承,页面里的所有元素都会继承这个红色文字;然后你用* { color: blue; }给所有元素设置了蓝色文字,最终所有元素的文字都会是蓝色,因为通配符选择器的优先级,比继承的样式优先级要高,哪怕继承的样式是写在后面的。新手很容易在这里踩坑,搞不懂为什么继承的样式不生效。
2.4.6 适用场景:只有一个推荐的场景,绝对不能滥用
通配符选择器的适用场景非常单一,唯一推荐的使用场景,就是用它来做 CSS 全局重置,清除所有元素的默认外边距和内边距,以及统一盒子模型的计算方式,也就是我们上面的示例 2 里的写法。
除了这个场景之外,其他任何场景都不推荐使用通配符选择器,尤其是不要用它来设置全局的字体、颜色、文字大小等样式,会导致很多意外的问题和性能消耗。
2.4.7 踩坑指南
- 滥用通配符选择器,给所有元素设置通用样式 :很多新手图省事,用
* { color: red; font-size: 14px; }给所有元素设置样式,结果导致表单元素、表格元素等出现意外的样式问题,优先级混乱,一定要避免,通配符选择器只用来清除默认的内外边距就行。 - 搞不懂通配符选择器的优先级,以为它的优先级很高 :很多新手以为通配符选择器选中了所有元素,优先级就很高,结果写了
* { color: red; },又写了p { color: blue; },发现 p 标签是蓝色的,就懵了。一定要记住,通配符选择器的优先级是最低的,比标签选择器还低,任何其他选择器都会覆盖它的同属性样式。 - 用通配符选择器设置的样式,被继承的样式覆盖了 :很多新手会反过来,写了
body { color: red; },又写了* { color: blue; },以为继承的样式会覆盖通配符的样式,结果所有元素都是蓝色的。一定要记住,通配符选择器的优先级,比继承的样式要高,只要通配符设置了同属性的样式,继承的样式就会被覆盖。 - 用通配符选择器设置
font-family等继承属性 :font-family、color、line-height这些属性,是可以继承的,只需要给body设置一次,所有子元素都会继承,完全不需要用通配符选择器来设置,用通配符反而会增加浏览器的解析负担,还会破坏表单元素的默认样式。
2.5 基础选择器终极小结:一张表记住所有核心知识点
到这里,我们已经把 4 种基础选择器,用保姆级的细节全部讲透了,为了让大家能更清晰地记住它们,我们给大家做了一张终极的小结对比表,把所有核心知识点都汇总起来,新手可以直接对照着看,一目了然:
| 选择器类型 | 语法格式 | 核心作用 | 唯一性 | 优先级(从高到低) | 核心优点 | 核心缺点 | 核心适用场景 |
|---|---|---|---|---|---|---|---|
| id 选择器 | #id名 |
选中页面里带对应 id 的唯一元素 | 绝对唯一,一个页面里同一个 id 只能给一个元素用 | 1(最高) | 精准度极高,优先级高,JS 获取元素效率最高 | 复用性为 0,优先级太高导致维护困难,无法组合 | 主要用于 JS 获取元素、页面锚点跳转 |
| 类选择器 | .类名 |
选中页面里所有带对应类名的元素 | 不唯一,一个类可以给多个元素用,一个元素可以用多个类 | 2 | 灵活度极高,复用性强,支持模块化组合,优先级适中 | 几乎没有缺点,只需要在 HTML 里加 class 属性 | 企业级开发里 90% 的 CSS 样式,都用类选择器实现 |
| 标签选择器 | HTML标签名 |
选中页面里所有同名的 HTML 标签 | 不唯一,选中所有同名标签 | 3 | 写法简单,批量生效,代码量极少 | 不能差异化选择,只能一刀切,优先级低 | 设置全局通用的基础样式、CSS 默认样式重置 |
| 通配符选择器 | * |
选中页面里的所有 HTML 元素 | 不唯一,选中所有元素 | 4(最低) | 一次性选中所有元素,代码极其简洁 | 优先级极低,可能影响性能,容易导致意外样式问题 | 仅推荐用于 CSS 全局重置,清除所有元素的默认内外边距 |
三、复合选择器保姆级全解析:实现更精准、更灵活的元素选择
我们已经把基础选择器全部讲透了,基础选择器就像是单个的积木块,能实现基础的元素选择,但是在实际开发中,我们会遇到很多更复杂的场景:
- 我只想选中
<div>里的<p>标签,不想选中页面里其他的<p>标签; - 我只想选中
<div>的亲儿子<a>标签,不想选中<div>里的孙子<a>标签; - 我想给
<div>、<p>、<h1>、.btn这些完全不同的选择器,设置同样的样式,不想重复写代码; - 我想给链接的不同状态设置不同的样式,比如鼠标悬停时变色,点击时变色。
这些场景,只用基础选择器是很难实现的,或者说实现起来非常麻烦,这时候我们就需要用到复合选择器 ------ 把两个或多个基础选择器,按照固定的规则组合起来,实现更精准、更灵活、更复杂的元素选择。
3.1 后代选择器(包含选择器):最常用的复合选择器,选中父元素里的所有后代
3.1.1 核心定义:什么是后代选择器?
后代选择器,也叫包含选择器,是实际开发中最常用的复合选择器,没有之一。它的核心定义是:把两个或多个基础选择器用空格分隔,选中前面的父元素里的所有后代元素,不管是亲儿子、孙子、重孙子,只要是这个父元素的后代,都会被选中。
这里的 "后代",指的是嵌套在父元素里面的所有元素,不管嵌套了多少层,只要是在父元素的开始标签和结束标签之间的元素,都是这个父元素的后代。
比如我们写div p,这个后代选择器,会选中所有嵌套在<div>标签里的<p>标签,不管这个<p>是<div>的直接子元素(亲儿子),还是嵌套在<div>里的<ul>里的<li>里的(孙子、重孙子),都会被选中。
3.1.2 生活化类比:一秒搞懂后代选择器的本质
我们还是用生活化的类比,让大家彻底理解后代选择器的本质:
后代选择器,就像是 **"爷爷找所有和自己有血缘关系的后代"**。爷爷要找所有自己的后代,不管是自己的亲生儿子,还是儿子的儿子(孙子),还是孙子的儿子(重孙子),只要是和自己有血缘关系的后代,都会被找出来,没有层数限制。
这个例子里的核心要素,和后代选择器完全对应:
- "爷爷" :就是后代选择器里的父元素选择器,写在空格的前面;
- "所有的后代(儿子、孙子、重孙子)" :就是后代选择器里的后代元素选择器,写在空格的后面;
- "不管嵌套多少层,只要是后代都找出来":就是后代选择器的核心特性,不管后代元素嵌套了多少层,都会被选中。
3.1.3 语法格式:用空格分隔多个基础选择器
后代选择器的语法格式非常简单,用空格来分隔两个或多个基础选择器,空格前面的是父元素选择器,空格后面的是后代元素选择器,格式如下:
父元素选择器 后代元素选择器 {
样式属性名1: 样式属性值1;
样式属性名2: 样式属性值2;
}
这里给大家补充几个核心的语法细节,新手一定要记牢:
- 多个选择器之间,必须用空格分隔,绝对不能用其他符号:空格是后代选择器的标志,浏览器看到选择器之间的空格,就知道这是后代选择器。如果用了逗号、大于号等其他符号,就变成了其他的选择器,完全不一样的效果,一定要注意。
- 父元素选择器和后代元素选择器,可以是任意的基础选择器:标签选择器、类选择器、id 选择器都可以,没有任何限制。比如:
- 标签选择器 + 标签选择器:
div p; - 类选择器 + 标签选择器:
.nav li; - id 选择器 + 标签选择器:
#header a; - 类选择器 + 类选择器:
.box .item;
- 标签选择器 + 标签选择器:
- 后代选择器可以有多个层级,不止两个 :比如
div ul li a,这是 4 个层级的后代选择器,选中<div>里的<ul>里的<li>里的<a>标签,完全合法,没有层数限制。 - 后代选择器只会选中空格后面的后代元素,不会选中空格前面的父元素 :比如
div p,只会给<p>标签设置样式,不会给<div>标签设置任何样式,父元素完全不受影响。
3.1.4 完整可运行代码示例:从基础到进阶,全覆盖
我们给大家准备了 4 个不同难度的代码示例,从最基础的用法,到实际开发中最常用的场景,全部覆盖,大家可以直接复制运行,亲眼看到后代选择器的效果。
示例 1:最基础的后代选择器用法,选中父元素里的亲儿子
这个示例是最基础的,给大家演示后代选择器最核心的用法,选中父元素里的亲儿子元素,实现差异化选择:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>后代选择器基础用法示例</title>
<style>
/* 标签选择器:给所有li标签设置通用基础样式 */
li {
font-size: 30px;
line-height: 1.5;
margin: 10px 0;
font-family: "微软雅黑", sans-serif;
}
/* 后代选择器:选中所有ol标签里的所有li标签,设置红色文字 */
ol li {
color: red;
}
/* 后代选择器:选中所有ul标签里的所有li标签,设置蓝色文字 */
ul li {
color: blue;
}
</style>
</head>
<body>
<!-- 这个ul里的li,会被ul li选中,文字变成蓝色 -->
<ul>
<li>aaa(蓝色,ul里的li)</li>
<li>bbb(蓝色,ul里的li)</li>
<li>ccc(蓝色,ul里的li)</li>
</ul>
<!-- 这个ol里的li,会被ol li选中,文字变成红色 -->
<ol>
<li>ddd(红色,ol里的li)</li>
<li>eee(红色,ol里的li)</li>
<li>fff(红色,ol里的li)</li>
</ol>
</body>
</html>
大家运行这段代码就能看到,同样都是<li>标签,在<ul>里的是蓝色,在<ol>里的是红色,完美实现了差异化选择。如果只用标签选择器li,只能给所有的<li>设置同样的颜色,没法区分开,而后代选择器完美解决了这个问题。
示例 2:后代选择器选中孙子、重孙子元素,无层数限制
这个示例给大家演示后代选择器的核心特性:不管嵌套多少层,只要是后代,都会被选中,哪怕是孙子、重孙子元素。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>后代选择器:选中孙子元素示例</title>
<style>
/* 标签选择器:给所有a标签设置通用基础样式 */
a {
font-size: 30px;
line-height: 1.5;
margin: 10px 0;
display: block;
font-family: "微软雅黑", sans-serif;
}
/* 后代选择器:选中所有ul标签里的所有a标签,不管这个a嵌套了多少层 */
ul a {
color: yellow;
/* 去掉a标签的默认下划线 */
text-decoration: none;
font-weight: bold;
}
</style>
</head>
<body>
<ul>
<li>aaa</li>
<li>bbb</li>
<!-- 这个a标签,是ul的孙子元素(ul → li → a),嵌套了2层,还是会被ul a选中 -->
<li><a href="#">ccc(孙子元素,黄色、加粗、无下划线)</a></li>
<li>
<div>
<p>
<!-- 这个a标签,是ul的重孙子元素(ul → li → div → p → a),嵌套了4层,还是会被ul a选中 -->
<a href="#">ddd(重孙子元素,黄色、加粗、无下划线)</a>
</p>
</div>
</li>
</ul>
<!-- 这个a标签,不在ul里面,不会被ul a选中,用默认样式 -->
<a href="#">eee(不在ul里,默认蓝色、有下划线)</a>
</body>
</html>
大家运行这段代码就能看到,哪怕是嵌套了 4 层的重孙子<a>标签,只要是在<ul>里面,都会被ul a这个后代选择器选中,套用黄色、加粗、无下划线的样式;而不在<ul>里面的<a>标签,完全不受影响,完美体现了后代选择器 "无层数限制,只要是后代就选中" 的核心特性。
这里还要给大家补充:这个示例里,我们也可以写成ul li a,效果是一样的,都是选中<ul>里的<li>里的<a>标签。ul a的写法更简洁,ul li a的写法更精准,大家可以根据自己的需求选择。
示例 3:后代选择器组合类选择器、id 选择器,实现更精准的定位
这个示例给大家演示后代选择器最常用的写法:组合类选择器、id 选择器,实现更精准的定位,这也是企业级开发里最常用的写法。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>后代选择器组合类选择器示例</title>
<style>
/* 标签选择器:给所有li标签设置通用基础样式 */
li {
font-size: 30px;
line-height: 1.5;
margin: 10px 0;
font-family: "微软雅黑", sans-serif;
}
/* 标签选择器:给所有a标签设置通用基础样式 */
a {
text-decoration: none;
font-size: 30px;
}
/* 后代选择器:组合类选择器,选中所有带class="one"的元素里的li标签,设置红色文字 */
.one li {
color: red;
}
/* 后代选择器:组合类选择器,选中所有带class="one"的元素里的a标签,设置绿色文字 */
.one a {
color: green;
}
/* 后代选择器:组合类选择器,选中带class="two"的元素里的带class="active"的li标签,设置蓝色文字、加粗 */
.two li.active {
color: blue;
font-weight: bold;
}
</style>
</head>
<body>
<!-- 这个ol带class="one",里面的li和a,会被.one li和.one a选中 -->
<ol class="one">
<li>ddd(红色,.one里的li)</li>
<li>eee(红色,.one里的li)</li>
<li><a href="#">fff(绿色,.one里的a)</a></li>
<li><a href="#">fff(绿色,.one里的a)</a></li>
</ol>
<!-- 这个ol没有带class="one",里面的li和a,不会被上面的后代选择器选中,用默认样式 -->
<ol>
<li>ddd(默认黑色,没有class="one")</li>
<li><a href="#">fff(默认蓝色,没有class="one")</a></li>
</ol>
<!-- 这个ul带class="two",里面带class="active"的li,会被.two li.active选中 -->
<ul class="two">
<li>列表项1(默认黑色)</li>
<li class="active">列表项2(蓝色、加粗,带active类)</li>
<li>列表项3(默认黑色)</li>
</ul>
</body>
</html>
大家运行这段代码就能看到,通过类选择器和后代选择器的组合,我们可以实现极其精准的定位:
- 只有带
class="one"的<ol>里的<li>和<a>,会被选中,其他的<ol>完全不受影响; - 只有带
class="two"的<ul>里,带class="active"的<li>,会被选中,其他的<li>完全不受影响。
这种写法,在企业级开发里用得最多,比如导航栏里的菜单项、列表里的选中项、卡片里的标题,都是用这种 "类选择器 + 后代选择器" 的写法,既精准,又灵活,还不会污染全局的样式。
3.1.5 作用特点:灵活度极高,是复合选择器里的 "万金油"
后代选择器的特点非常突出,优点极多,缺点很少,是复合选择器里用得最多的 "万金油"。
优点 1:可以精准选中某个父元素里的所有后代元素,不管嵌套多少层,灵活度极高
这是后代选择器最大的优点,它可以突破嵌套层数的限制,只要是在父元素里面的后代元素,都能选中,灵活度极高,能应对各种复杂的 HTML 结构。
优点 2:可以组合任意的基础选择器,精准度可以无限提升
后代选择器可以组合标签选择器、类选择器、id 选择器,你想要多精准,就能写多精准。比如你只想选中页面里,id 为header的头部里,class 为nav的导航栏里,class 为menu的菜单里的<a>标签,只需要写#header .nav .menu a,就能精准选中,不会影响页面里其他任何的<a>标签。
优点 3:只会影响后代元素,不会影响父元素,完全不会污染父元素的样式
后代选择器只会给空格后面的后代元素设置样式,完全不会影响空格前面的父元素,也不会影响父元素外面的其他元素,样式隔离性非常好,不会出现全局样式污染的问题。
优点 4:写法简单,学习成本低,和 HTML 的嵌套结构完全对应
后代选择器的写法,和 HTML 的嵌套结构是完全对应的,你只要能看懂 HTML 的嵌套关系,就能写出对应的后代选择器,学习成本极低,非常容易上手。
缺点 1:如果后代层级写得太深,选择器会变得很长,可读性会变差
比如div ul li p span a,这个选择器写得很长,嵌套了 5 层,可读性会稍微差一点,后期维护起来也会有点麻烦。不过这种情况很少见,我们可以通过给元素加类名,来简化选择器的写法,比如给最里面的 a 标签加个类名.link,直接写.link就行,不用写这么长的后代选择器。
缺点 2:如果父元素选择器写得太宽泛,会导致样式污染
比如你写了div a,会选中页面里所有<div>里的<a>标签,包括头部、内容区、页脚里的所有<a>,如果你只想选中内容区里的<a>,就会导致样式污染,影响到其他的<a>标签。不过这个问题很好解决,只要给父元素加个类名,用类选择器限定范围就行,比如.content a,就只会选中内容区里的<a>标签。
3.1.6 适用场景:几乎所有需要限定范围的元素选择,都能用
后代选择器的适用场景极其广泛,在企业级开发里,90% 以上的复合选择器,都是后代选择器,我们给大家总结了它的核心适用场景:
- 选中某个父元素里的所有同名后代元素 :比如选中导航栏里的所有
<a>标签,选中商品列表里的所有<li>标签,选中文章内容里的所有<p>标签。 - 差异化选择不同父元素里的同名元素 :比如
<ul>里的<li>是一种样式,<ol>里的<li>是另一种样式;头部里的<a>是白色的,内容区里的<a>是蓝色的。 - 限定样式的生效范围,避免全局样式污染 :比如你想给某个模块里的按钮设置特殊样式,只需要写
.module .btn,就只会选中这个模块里的按钮,不会影响页面里其他的按钮,完美避免样式污染。 - 组合类选择器、id 选择器,实现精准的元素定位:这是最常用的场景,通过类名限定父元素的范围,再选中里面的后代元素,精准度极高,不会影响其他元素。
3.1.7踩坑指南
- 后代选择器的多个选择器之间,没有用空格分隔,用了其他符号 :比如把
div p写成了divp、div,p、div>p,这些都不是后代选择器,完全不一样的效果,一定要记住,后代选择器的标志是空格,必须用空格分隔多个选择器。 - 搞反了父元素和后代元素的顺序 :比如你想选中
<div>里的<p>标签,结果写成了p div,这会选中<p>里的<div>标签,完全和你的需求相反,一定要记住,父元素选择器写在前面,后代元素选择器写在后面,和 HTML 的嵌套顺序一致。 - 后代选择器写得太长,嵌套层级太深 :比如写了
body div.main div.content ul li p a,嵌套了 7 层,可读性极差,后期维护起来非常麻烦,而且浏览器解析的性能也会受影响。推荐大家后代选择器的层级尽量不要超过 3 层,超过 3 层的话,最好通过加类名来简化。 - 父元素选择器写得太宽泛,导致全局样式污染 :比如写了
div a,结果页面里所有<div>里的<a>都被改了样式,包括你不想改的元素。一定要给父元素加一个唯一的类名,用类选择器来限定范围,比如.content a,避免样式污染。 - 以为后代选择器只会选中亲儿子,不会选中孙子元素 :很多新手刚学的时候,以为
div p只会选中<div>的直接子元素<p>,结果孙子元素里的<p>也被选中了,就懵了。一定要记住,后代选择器会选中所有的后代,不管嵌套多少层,只要是在父元素里面的,都会被选中。如果只想选中亲儿子,要用子选择器,不是后代选择器。 - 后代选择器里的类名拼写错误 :比如 CSS 里写的是
.nav li,HTML 里的类名是class="nev",拼写错了,自然匹配不上,样式不生效,一定要仔细检查类名的拼写,确保 CSS 和 HTML 里的完全一致。 - 以为后代选择器会影响父元素 :很多新手写了
div p { color: red; },结果发现<div>的文字没有变成红色,就以为样式不生效。一定要记住,后代选择器只会给空格后面的后代元素设置样式,不会影响前面的父元素,<div>不会有任何样式变化。
3.2 子选择器:只选亲儿子,不选孙子,精准度比后代选择器更高
3.2.1 核心定义:什么是子选择器?
子选择器,也叫直接子元素选择器,是和后代选择器非常像的一种复合选择器,但是有一个核心的、本质的区别:后代选择器会选中父元素里的所有后代(儿子、孙子、重孙子),而子选择器,只会选中父元素的直接子元素(亲儿子),不会选中孙子、重孙子等更深层级的后代元素。
这里的 "直接子元素(亲儿子)",指的是只嵌套了一层的元素,也就是父元素的开始标签之后,第一个嵌套的元素,中间没有其他的标签包裹。比如:
<div>
<!-- 这个a标签,是div的直接子元素,亲儿子 -->
<a href="#">链接1</a>
<p>
<!-- 这个a标签,是div的孙子元素,不是直接子元素 -->
<a href="#">链接2</a>
</p>
</div>
上面的代码里,第一个<a>是<div>的亲儿子,会被子选择器div > a选中;第二个<a>是<div>的孙子,不会被子选择器选中,但是会被后代选择器div a选中,这就是两者的核心区别。
3.2.2 生活化类比:一秒搞懂子选择器和后代选择器的区别
我们还是用生活化的类比,让大家一秒就能区分开子选择器和后代选择器:
子选择器,就像是 **"爷爷只找自己的亲生儿子,不找孙子、重孙子"**。爷爷要找的,只是自己直接生的儿子,只有这一层,儿子生的孙子、孙子生的重孙子,都不算,都不会被找出来。
而后代选择器,是爷爷找所有的后代,包括儿子、孙子、重孙子,这就是两者最核心的区别。
这个例子里:
- "爷爷" :就是子选择器里的父元素选择器,写在大于号的前面;
- "亲生儿子" :就是子选择器里的直接子元素选择器,写在大于号的后面;
- "只找亲儿子,不找孙子":就是子选择器的核心特性,只会选中直接子元素,不会选中更深层级的后代。
3.2.3 语法格式:用大于号>分隔多个基础选择器
子选择器的语法格式,和后代选择器很像,唯一的区别就是,用 ** 大于号>** 来分隔多个选择器,而不是空格,格式如下:
父元素选择器 > 直接子元素选择器 {
样式属性名1: 样式属性值1;
样式属性名2: 样式属性值2;
}
这里给大家补充几个核心的语法细节,新手一定要记牢,和后代选择器区分开:
- 多个选择器之间,必须用大于号
>分隔,绝对不能用空格 :大于号是子选择器的标志,浏览器看到>,就知道这是子选择器,只会选中直接子元素。如果用了空格,就变成了后代选择器,效果完全不一样,一定要注意区分。 - 大于号
>的前后,可以加空格,也可以不加,但是推荐加空格,可读性更强 :比如div>a和div > a,效果是完全一样的,但是div > a的写法,空格把大于号和选择器分开,可读性更强,更符合行业规范,推荐大家这么写。 - 父元素选择器和直接子元素选择器,也可以是任意的基础选择器 :标签选择器、类选择器、id 选择器都可以,没有任何限制,比如
.nav > li、#header > .nav、.box > .item。 - 子选择器也可以有多个层级,不止两个 :比如
div > ul > li,这是两层子选择器,选中<div>的直接子元素<ul>的直接子元素<li>,完全合法,但是层级多了之后,可读性会变差,不推荐写太多层级。 - 和后代选择器一样,子选择器只会选中大于号后面的直接子元素,不会选中大于号前面的父元素 :比如
div > a,只会给<a>标签设置样式,不会给<div>标签设置样式。
3.2.4 完整可运行代码示例:和后代选择器的核心对比
为了让大家彻底搞懂子选择器和后代选择器的核心区别,我们给大家准备了一个对比示例,这个示例非常重要,新手一定要仔细看,亲手运行一下,就能一秒搞懂两者的区别。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>子选择器 vs 后代选择器 核心对比示例</title>
<style>
/* 标签选择器:给所有a标签设置通用基础样式 */
a {
font-size: 30px;
line-height: 1.5;
margin: 10px 0;
text-decoration: none;
display: block;
font-family: "微软雅黑", sans-serif;
}
/* 后代选择器:选中.two里的所有a标签,不管是亲儿子还是孙子,设置红色文字 */
.two a {
color: red;
}
/* 子选择器:选中.two的直接子元素a标签,只选亲儿子,不选孙子,设置蓝色文字、加粗 */
.two > a {
color: blue;
font-weight: bold;
}
</style>
</head>
<body>
<div class="two">
<!-- 这个a标签,是.two的直接子元素(亲儿子):
1. 会被后代选择器.two a选中,设置红色;
2. 会被子选择器.two > a选中,设置蓝色、加粗;
3. 两个选择器的优先级相同,子选择器写在后面,根据层叠性,后面的覆盖前面的;
4. 最终生效的是:蓝色、加粗。
-->
<a href="#">链接1(亲儿子,蓝色、加粗)</a>
<p>
<!-- 这个a标签,是.two的孙子元素(.two → p → a):
1. 会被后代选择器.two a选中,设置红色;
2. 不会被子选择器.two > a选中,因为它不是.two的直接子元素;
3. 最终生效的是:红色,不加粗。
-->
<a href="#">链接2(孙子,红色、不加粗)</a>
</p>
</div>
</body>
</html>
大家把这段代码用浏览器打开,就能直观地看到子选择器和后代选择器的核心区别,这也是新手最容易搞混的地方:
- 链接 1 :是
.two的亲儿子,会被两个选择器都选中。因为两个选择器的优先级是相同的,子选择器写在后面,根据 CSS 的层叠性,后面写的样式会覆盖前面写的同属性样式,所以最终生效的是蓝色、加粗。 - 链接 2 :是
.two的孙子元素,只会被后代选择器.two a选中,不会被子选择器.two > a选中,所以最终生效的是红色、不加粗。
这个对比示例,完美展示了子选择器的核心特点:只选亲儿子,不选孙子,精准度比后代选择器更高。
3.2.5 子选择器的进阶用法示例:多层子选择器和实际开发场景
我们给大家准备了一个实际开发中最常用的子选择器示例:导航栏的一级菜单和二级菜单,用子选择器选中一级菜单,用后代选择器选中所有菜单,这是企业级开发里非常经典的用法。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>子选择器实际开发场景:导航栏菜单</title>
<style>
/* 全局重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
ul {
list-style: none;
}
a {
text-decoration: none;
color: #333;
}
/* 导航栏容器 */
.nav {
width: 100%;
height: 60px;
background-color: #333;
padding: 0 20px;
}
/* 子选择器:选中.nav的直接子元素li,也就是一级菜单,横向排列 */
.nav > li {
/* 左浮动,让一级菜单横向排列 */
float: left;
/* 每个一级菜单的高度和导航栏一致 */
height: 60px;
/* 每个一级菜单的左右内边距 */
padding: 0 20px;
/* 相对定位,给二级菜单的绝对定位做参考 */
position: relative;
line-height: 60px;
}
/* 子选择器:选中一级菜单里的直接子元素a,也就是一级菜单的链接,设置白色文字 */
.nav > li > a {
color: #fff;
font-size: 16px;
display: block;
height: 100%;
}
/* 鼠标悬停在一级菜单上时,一级菜单的背景变色 */
.nav > li:hover {
background-color: #1677ff;
}
/* 二级菜单容器,默认隐藏 */
.sub-nav {
/* 绝对定位,相对于一级菜单定位 */
position: absolute;
top: 60px;
left: 0;
width: 180px;
background-color: #fff;
border: 1px solid #d9d9d9;
/* 默认隐藏 */
display: none;
}
/* 后代选择器:选中二级菜单里的所有li,也就是二级菜单项 */
.sub-nav li {
height: 40px;
line-height: 40px;
padding: 0 15px;
}
/* 后代选择器:选中二级菜单里的所有a标签,设置黑色文字 */
.sub-nav a {
color: #333;
font-size: 14px;
display: block;
height: 100%;
}
/* 鼠标悬停在一级菜单上时,显示对应的二级菜单 */
.nav > li:hover .sub-nav {
display: block;
}
/* 鼠标悬停在二级菜单项上时,背景变色 */
.sub-nav li:hover {
background-color: #f5f5f5;
}
</style>
</head>
<body>
<!-- 导航栏 -->
<ul class="nav">
<!-- 一级菜单1 -->
<li>
<a href="#">首页</a>
</li>
<!-- 一级菜单2,带二级菜单 -->
<li>
<a href="#">产品中心</a>
<!-- 二级菜单,是li的子元素,是.nav的孙子元素 -->
<ul class="sub-nav">
<li><a href="#">产品1</a></li>
<li><a href="#">产品2</a></li>
<li><a href="#">产品3</a></li>
</ul>
</li>
<!-- 一级菜单3,带二级菜单 -->
<li>
<a href="#">关于我们</a>
<ul class="sub-nav">
<li><a href="#">公司介绍</a></li>
<li><a href="#">发展历程</a></li>
<li><a href="#">联系我们</a></li>
</ul>
</li>
<!-- 一级菜单4 -->
<li>
<a href="#">新闻动态</a>
</li>
</ul>
</body>
</html>
大家运行这段代码,就能看到一个非常经典的网站导航栏,有一级菜单和二级下拉菜单,这里面完美运用了子选择器和后代选择器的区别:
- 我们用子选择器
.nav > li,只选中了.nav的直接子元素<li>,也就是一级菜单,给它们设置了横向排列、高度、内边距,不会影响到二级菜单里的<li>; - 我们用子选择器
.nav > li > a,只选中了一级菜单里的直接子元素<a>,给它们设置了白色文字,不会影响到二级菜单里的<a>标签; - 我们用后代选择器
.sub-nav li和.sub-nav a,选中了二级菜单里的所有<li>和<a>,给它们设置了单独的样式。
如果这里不用子选择器,而是用后代选择器.nav li,就会把一级菜单和二级菜单里的所有<li>都选中,全部变成横向排列,二级菜单就完全乱了。这就是子选择器的核心价值:精准控制直接子元素,不会影响到更深层级的后代元素,完美实现样式隔离。
3.2.6 作用特点:精准度极高,完美实现样式隔离
子选择器的特点非常鲜明,核心就是精准度高,样式隔离性好。
优点 1:精准度极高,只会选中父元素的直接子元素,不会影响孙子、重孙子元素
这是子选择器最大的优点,也是它不可替代的原因。在很多复杂的 HTML 结构里,我们只想控制直接子元素的样式,不想影响到里面嵌套的其他元素,子选择器就能完美实现,不会出现样式污染的问题。
优点 2:样式隔离性极好,不会污染更深层级的元素样式
用子选择器设置的样式,只会作用于直接子元素,不会传递到更深的层级,哪怕里面的子元素有同名的标签,也不会被影响,样式隔离性非常好,在复杂的组件化开发里,非常有用。
优点 3:可以组合任意的基础选择器,也可以多层嵌套使用
和后代选择器一样,子选择器可以组合标签选择器、类选择器、id 选择器,也可以多层嵌套使用,比如.nav > li > a,灵活度也很高。
缺点 1:灵活度比后代选择器低,只能选中直接子元素
这是子选择器的局限性,它只能选中直接子元素,不能选中孙子、重孙子元素,灵活度不如后代选择器。但是在需要精准控制的场景里,这正是它的优点。
缺点 2:如果 HTML 结构发生变化,子选择器可能会失效
如果后期修改了 HTML 的嵌套结构,原来的直接子元素,现在被其他标签包裹了,变成了孙子元素,那么原来的子选择器就会失效,需要重新修改。而后代选择器,只要元素还在父元素里面,不管嵌套结构怎么变,都能选中,容错率更高。
3.2.7 适用场景:需要精准控制直接子元素,避免样式污染的场景
子选择器的适用场景非常明确,主要是以下几种:
- 导航栏的一级菜单和二级菜单:就像我们上面的示例,用子选择器控制一级菜单的样式,不会影响二级菜单,这是最经典的适用场景。
- 只需要控制父元素的直接子元素,不想影响里面嵌套的其他元素 :比如一个卡片容器,我们只想给它的直接子元素设置上下外边距,不想影响卡片里面嵌套的其他元素的间距,就可以用子选择器
.card > *。 - 避免样式污染,限制样式的作用层级:在复杂的页面里,用子选择器可以精准控制样式的作用层级,不会不小心影响到更深层级的元素,避免全局样式污染。
- 组件化开发里的样式隔离:在 Vue、React 等组件化开发里,子选择器经常用来控制组件根元素的直接子元素的样式,不会影响到组件内部嵌套的子组件的样式,实现样式隔离。
3.2.8 踩坑指南
- 把子选择器的大于号
>写成了空格,变成了后代选择器 :这是新手最容易犯的错误,把.two > a写成了.two a,结果孙子元素也被选中了,和预期的效果不一样,一定要记住,子选择器的标志是大于号>,不是空格。 - 大于号
>的前后没有加空格,可读性极差 :比如写成div>a,虽然效果一样,但是可读性很差,推荐大家写成div > a,大于号前后加空格,代码更清晰,更容易维护。 - 搞反了父元素和子元素的顺序 :比如想选中
<div>的亲儿子<a>,结果写成了a > div,这会选中<a>的亲儿子<div>,完全和需求相反,一定要记住,父元素写在前面,子元素写在后面。 - 以为子选择器会选中孙子元素:很多新手刚学的时候,以为子选择器和后代选择器一样,会选中所有的后代,结果孙子元素没有被选中,就以为样式不生效。一定要记住,子选择器只会选中直接子元素,孙子元素不会被选中,想要选中孙子元素,要用后代选择器。
- 子选择器的层级写得太多,可读性极差 :比如写成
div > ul > li > p > span > a,嵌套了 6 层,可读性极差,后期维护起来非常麻烦,推荐子选择器的层级尽量不要超过 3 层。 - HTML 结构发生变化,子选择器失效了,不知道为什么 :比如原来的
<a>是<div>的直接子元素,后来加了一个<p>把<a>包裹起来,<a>变成了孙子元素,原来的子选择器div > a就失效了,这时候要么修改 HTML 结构,要么把选择器改成后代选择器div a。
3.3 并集选择器(集体声明):给多个选择器设置相同的样式,大幅简化代码
3.3.1 核心定义:什么是并集选择器?
并集选择器,也叫集体声明、分组选择器,是一种用来简化代码的复合选择器,它的核心定义是:把多个不同的选择器,用逗号,分隔开,给这些选择器对应的所有元素,设置完全相同的样式。
简单来说,如果你有多个不同的选择器,需要设置完全一样的样式,就可以把它们用逗号合并起来,只写一遍样式声明,不用给每个选择器都重复写一遍一模一样的样式,大幅减少代码冗余,提升代码的可维护性。
比如,你想给<div>、<p>、<h1>、.btn这四个选择器,都设置font-size: 16px; line-height: 1.5;,如果不用并集选择器,你要写 4 遍:
html
div {
font-size: 16px;
line-height: 1.5;
}
p {
font-size: 16px;
line-height: 1.5;
}
h1 {
font-size: 16px;
line-height: 1.5;
}
.btn {
font-size: 16px;
line-height: 1.5;
}
用了并集选择器之后,只需要写一遍:
div,
p,
h1,
.btn {
font-size: 16px;
line-height: 1.5;
}
效果完全一样,但是代码量大幅减少,没有任何冗余,后期要修改的时候,只需要改一遍,所有选择器都会同步更新,维护成本极低。
3.3.2 生活化类比:一秒搞懂并集选择器的本质
并集选择器,就像是学校给多个班级同时发同一个通知。比如学校要给高一一班、高一二班、高一三班,同时发一个通知:"明天早上 8 点,所有同学都到操场集合"。学校不用给每个班级单独发一遍一模一样的通知,只需要开一个广播,同时通知这三个班级就行,效率极高。
这个例子里的核心要素,和并集选择器完全对应:
- "高一一班、高一二班、高一三班" :就是并集选择器里的多个不同的选择器,用逗号分隔;
- "同时发同一个通知" :就是给这些选择器设置完全相同的样式声明,只需要写一遍;
- "不用单独给每个班级发通知" :就是不用给每个选择器重复写一遍相同的样式,大幅简化代码。
3.3.3 语法格式:用逗号,分隔多个选择器
并集选择器的语法格式非常简单,用 ** 逗号,** 来分隔多个不同的选择器,格式如下:
选择器1,
选择器2,
选择器3 {
样式属性名1: 样式属性值1;
样式属性名2: 样式属性值2;
}
这里给大家补充几个核心的语法细节,新手一定要记牢:
-
多个选择器之间,必须用逗号
,分隔,绝对不能用空格、大于号等其他符号:逗号是并集选择器的标志,浏览器看到逗号,就知道这是并集选择器,会把逗号前后的选择器,都应用后面的样式。如果用了其他符号,就变成了其他的选择器,效果完全不一样。 -
推荐每个选择器单独占一行,竖着写,不要横着写 :比如上面的示例,每个选择器占一行,逗号放在上一行的结尾,这样的写法,可读性极强,一眼就能看清有哪些选择器,后期要加、减选择器的时候,也非常方便。如果横着写
div, p, h1, .btn { ... },选择器多了之后,可读性会变得极差,非常不推荐。 -
最后一个选择器的后面,绝对不能加逗号 :这是新手最容易犯的错误,最后一个选择器的后面加了逗号,比如:
div, p, h1, { /* 这里最后一个选择器h1后面加了逗号,是错误的 */ color: red; }虽然部分现代浏览器会自动忽略这个多余的逗号,但是很多老版本的浏览器(比如 IE),会解析失败,导致整个样式块完全不生效,一定要注意,最后一个选择器的后面,绝对不能加逗号。
-
逗号分隔的每个选择器,可以是任意类型的选择器 :标签选择器、类选择器、id 选择器、后代选择器、子选择器,都可以放在并集选择器里,没有任何限制,比如:
/* 并集选择器里,包含了标签选择器、类选择器、后代选择器、子选择器 */ p, .btn, .nav li, .box > a { color: red; font-size: 16px; } -
并集选择器里的每个选择器,都是完全独立的,互相之间没有任何影响 :浏览器会把并集选择器拆开来,分别解析每个选择器,然后给每个选择器对应的元素,应用后面的样式。比如
div, p { color: red; },浏览器会拆成div { color: red; }和p { color: red; },分别执行,两个选择器之间没有任何关联,不会互相影响。
3.3.4 完整可运行代码示例:从基础到进阶,全覆盖
我们给大家准备了 3 个不同难度的代码示例,从最基础的用法,到实际开发中最常用的场景,全部覆盖,大家可以直接复制运行,亲眼看到并集选择器的强大之处。
示例 1:最基础的并集选择器用法,简化重复代码
这个示例是最基础的,给大家演示并集选择器最核心的作用:简化重复的代码,给多个选择器设置相同的样式。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>并集选择器基础用法示例</title>
<style>
/* 不用并集选择器的写法:重复写了3遍一模一样的样式,代码冗余,维护麻烦 */
/*
div {
color: red;
font-size: 30px;
line-height: 1.5;
margin: 20px 0;
}
h3 {
color: red;
font-size: 30px;
line-height: 1.5;
margin: 20px 0;
}
ul > li {
color: red;
font-size: 30px;
line-height: 1.5;
margin: 20px 0;
}
*/
/* 用并集选择器的写法:把相同的样式合并起来,只写一遍,代码简洁,维护方便 */
div,
h3,
ul > li {
color: red;
font-size: 30px;
line-height: 1.5;
margin: 20px 0;
font-family: "微软雅黑", sans-serif;
}
</style>
</head>
<body>
<!-- div标签,会被并集选择器选中,红色30px -->
<div>苹果(div标签,红色30px)</div>
<!-- h3标签,会被并集选择器选中,红色30px -->
<h3>香蕉(h3标签,红色30px)</h3>
<ul>
<!-- li标签,是ul的直接子元素,会被并集选择器里的ul > li选中,红色30px -->
<li>鸭梨(ul里的li,红色30px)</li>
<li>橙子(ul里的li,红色30px)</li>
</ul>
</body>
</html>
大家运行这段代码就能看到,用并集选择器的写法,和不用并集选择器的写法,效果完全一样,但是代码量大幅减少,没有任何冗余。后期如果要把文字颜色从红色改成蓝色,只需要修改一行代码,三个选择器都会同步更新,不用一个一个修改,维护成本极低。
示例 2:并集选择器组合不同类型的选择器,实现复杂的批量样式设置
这个示例给大家演示,并集选择器可以组合任意类型的选择器,包括标签选择器、类选择器、id 选择器、后代选择器、子选择器,灵活度极高。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>并集选择器组合不同类型选择器示例</title>
<style>
/* 全局重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
a {
text-decoration: none;
}
ul {
list-style: none;
}
/* 并集选择器:组合了标签选择器、类选择器、id选择器、后代选择器、子选择器,给它们设置相同的文字颜色和大小 */
p,
.title,
#header,
.nav li a,
.card > h3 {
color: #333;
font-size: 16px;
line-height: 1.5;
font-family: "微软雅黑", sans-serif;
}
/* 给不同的选择器,设置单独的特殊样式 */
.title {
font-size: 24px;
font-weight: bold;
margin: 20px 0;
text-align: center;
}
#header {
height: 60px;
background-color: #f5f5f5;
padding: 0 20px;
line-height: 60px;
}
.nav {
float: right;
}
.nav li {
float: left;
margin: 0 10px;
}
.card {
width: 300px;
border: 1px solid #d9d9d9;
border-radius: 8px;
padding: 20px;
margin: 20px auto;
}
.card > h3 {
font-size: 20px;
font-weight: bold;
margin-bottom: 10px;
color: #1677ff;
}
</style>
</head>
<body>
<div id="header">
<ul class="nav">
<li><a href="#">首页</a></li>
<li><a href="#">产品</a></li>
<li><a href="#">关于我们</a></li>
</ul>
</div>
<h2 class="title">并集选择器示例</h2>
<div class="card">
<h3>卡片标题</h3>
<p>卡片内容,这是一段测试文字,用来演示并集选择器的效果。</p>
</div>
</body>
</html>
大家运行这段代码就能看到,我们用并集选择器,给完全不同类型的 5 个选择器,设置了统一的基础字体样式:颜色、大小、行高、字体,不用给每个选择器都重复写一遍。然后再给每个选择器,单独设置它们的特殊样式,比如标题的加粗、卡片标题的蓝色,代码结构极其清晰,公共样式只写一遍,特殊样式单独设置,复用性和可维护性拉满。
这就是企业级开发里最常用的写法:用并集选择器提取公共样式,用单独的选择器设置特殊样式,代码简洁、逻辑清晰、维护方便。
示例 3:并集选择器在 CSS 重置里的经典用法
这个示例给大家演示,并集选择器在 CSS 重置里的经典用法,也是我们实际开发中每天都会用到的场景。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>并集选择器CSS重置示例</title>
<style>
/* 用并集选择器,给所有的标题标签,统一清除默认的加粗和外边距 */
h1, h2, h3, h4, h5, h6 {
margin: 0;
font-weight: normal;
}
/* 用并集选择器,给所有的列表标签,统一清除默认的内外边距和列表符号 */
ul, ol, dl {
margin: 0;
padding: 0;
list-style: none;
}
/* 用并集选择器,给所有的表格标签,统一设置边框合并 */
table, tr, td, th {
border-collapse: collapse;
border-spacing: 0;
}
/* 用并集选择器,给所有的表单元素,统一清除默认的边框和轮廓 */
input, button, textarea, select {
border: none;
outline: none;
font-family: inherit;
}
/* 全局基础样式 */
body {
margin: 0;
font-family: "微软雅黑", "PingFang SC", sans-serif;
color: #333;
font-size: 14px;
line-height: 1.5;
}
a {
text-decoration: none;
color: #333;
}
img {
display: block;
}
</style>
</head>
<body>
<!-- 所有的标题标签,都清除了默认的加粗和外边距 -->
<h1>一级标题</h1>
<h2>二级标题</h2>
<h3>三级标题</h3>
<!-- 所有的列表标签,都清除了默认的内外边距和列表符号 -->
<ul>
<li>无序列表项1</li>
<li>无序列表项2</li>
</ul>
<ol>
<li>有序列表项1</li>
<li>有序列表项2</li>
</ol>
</body>
</html>
大家运行这段代码就能看到,我们用并集选择器,给所有的标题标签、列表标签、表格标签、表单元素,统一设置了重置样式,不用给每个标签单独写一遍,代码极其简洁,这是并集选择器在 CSS 重置里的经典用法,几乎所有的前端项目里,都会这么写。
3.3.5 作用特点:全是优点,没有缺点
并集选择器的特点,几乎全是优点,没有任何实质性的缺点,是每个前端开发者都必须熟练掌握的简化代码的神器。
优点 1:大幅减少代码冗余,让代码更简洁
这是并集选择器最大的优点,它可以把多个选择器里相同的样式,合并起来只写一遍,完全消除了重复的代码,让 CSS 文件的体积更小,代码更简洁。
优点 2:大幅降低代码的维护成本,提升开发效率
用了并集选择器之后,后期要修改公共样式的时候,只需要修改一遍,所有的选择器都会同步更新,不用一个一个去修改,不仅节省了大量的时间,还避免了漏改、错改的问题,维护成本极低,开发效率大幅提升。
优点 3:代码逻辑更清晰,可读性更强
用并集选择器把公共样式合并起来,能让代码的逻辑更清晰,一眼就能看出哪些选择器用了相同的公共样式,哪些是单独的特殊样式,代码的可读性和可维护性大幅提升。
优点 4:可以组合任意类型的选择器,没有任何限制
并集选择器可以组合标签选择器、类选择器、id 选择器、后代选择器、子选择器,甚至是其他的复合选择器,没有任何限制,灵活度极高,能应对所有的场景。
缺点:没有任何实质性的缺点,唯一需要注意的就是写法规范,不要在最后一个选择器后面加逗号,不要横着写太多选择器导致可读性变差,这些都只是写法上的注意事项,不是选择器本身的缺点。
3.3.6 适用场景:只要有重复的样式,就可以用
并集选择器的适用场景极其广泛,只要你有多个不同的选择器,需要设置相同的样式,就可以用并集选择器,我们给大家总结了最核心的几个适用场景:
- 提取多个选择器的公共样式,消除代码冗余:这是最核心、最常用的场景,只要有重复的样式,就用并集选择器合并起来。
- CSS 全局重置,给多个标签设置统一的重置样式:比如给所有的标题标签、列表标签、表单元素,设置统一的重置样式,这是并集选择器的经典用法。
- 给页面里多个不同的元素,设置统一的基础样式:比如给页面里所有的按钮、输入框、卡片,设置统一的字体、行高、圆角,用并集选择器合并起来,只写一遍。
- 给多个不同的状态,设置相同的样式 :比如给链接的
:link、:visited、:active状态,设置相同的文字颜色,用并集选择器合并起来。
简单来说:只要你发现自己在给多个不同的选择器,写一模一样的样式,就一定要用并集选择器把它们合并起来,这是前端开发的基本素养。
3.3.7 踩坑指南
- 多个选择器之间,用了空格而不是逗号,变成了后代选择器 :这是新手最容易犯的错误,把
div, p写成了div p,结果变成了后代选择器,选中了 div 里的 p 标签,完全和预期的效果不一样,一定要记住,并集选择器的标志是逗号,,不是空格。 - 最后一个选择器的后面,加了多余的逗号 :很多新手在竖着写的时候,会给最后一个选择器的后面也加一个逗号,比如
div, p, { ... },这会导致老版本的浏览器解析失败,样式完全不生效,一定要注意,最后一个选择器的后面,绝对不能加逗号。 - 把所有的选择器都横着写在一行里,可读性极差 :比如
div, p, h1, .btn, .nav li, .card > a { ... },选择器多了之后,一行根本看不完,可读性极差,后期要加、减选择器的时候,也非常麻烦。一定要推荐每个选择器单独占一行,竖着写,代码更清晰,可读性更强。 - 以为并集选择器里的选择器是有关联的,结果理解错了效果 :很多新手以为
div, p { color: red; }是选中 div 里的 p 标签,其实不是,它是选中所有的 div 和所有的 p 标签,两个选择器是完全独立的,没有任何关联,一定要搞清楚,并集选择器的逗号是 "和" 的意思,不是 "里的" 的意思。 - 并集选择器里的选择器拼写错误 :比如 CSS 里写的是
.btn, .title { ... },HTML 里的类名是.titel,拼写错了,自然匹配不上,样式不生效,一定要仔细检查每个选择器的拼写。 - 滥用并集选择器,把完全不相关的选择器合并在一起:很多新手为了省事,把页面里所有的选择器都合并到一个并集选择器里,结果导致代码逻辑混乱,后期要修改的时候,根本找不到对应的选择器,维护成本反而更高。一定要记住,只有设置完全相同样式的选择器,才需要合并到并集选择器里,完全不相关的选择器,不要随便合并。
3.4 伪类选择器:给元素的不同状态设置样式,实现基础交互效果
3.4.1 核心定义:什么是伪类选择器?
伪类选择器,是一种特殊的选择器,它的核心定义是:用来选中 HTML 元素的特定状态,给这个特定状态设置单独的样式。
这里的 "特定状态",指的是元素的动态行为、位置状态等,比如:
- 链接的未访问状态、已访问状态;
- 鼠标悬停在元素上的状态;
- 鼠标按下元素、还没松开的状态;
- 输入框获得焦点的状态;
- 元素是父元素的第几个子元素的状态。
这些状态,是无法通过基础选择器、其他复合选择器来选中的,只有伪类选择器能选中,这也是伪类选择器的核心价值 ------ 它能让我们的页面,从静态的样式,变成有交互反馈的动态样式,大幅提升用户体验。
入门阶段,我们重点掌握链接伪类选择器,这是最基础、最常用的伪类选择器
3.4.2 链接伪类选择器的四种状态
链接伪类选择器,是专门给<a>标签(超链接)用的,它对应了链接的四种不同的状态,我们给大家一个一个讲透:
| 伪类选择器 | 核心作用 | 触发条件 |
|---|---|---|
a:link |
选中未被访问过的链接 | 这个链接的地址,用户从来没有点击访问过,浏览器的历史记录里没有这个地址 |
a:visited |
选中已经被访问过的链接 | 这个链接的地址,用户已经点击访问过,浏览器的历史记录里有这个地址 |
a:hover |
选中鼠标指针悬停在上面的链接 | 用户把鼠标指针移到了链接上,但是还没有点击,鼠标还在链接的范围内 |
a:active |
选中活动状态的链接 | 用户用鼠标左键按下了链接,但是还没有松开鼠标左键,这个短暂的状态 |
这四个状态,覆盖了用户和链接交互的完整流程:用户看到未访问的链接 → 把鼠标移到链接上 → 按下鼠标点击链接 → 松开鼠标,访问链接,链接变成已访问状态。我们可以给这四个不同的状态,设置不同的样式,给用户清晰的交互反馈,提升用户体验。
3.4.3 语法格式:冒号:连接元素选择器和伪类名
链接伪类选择器的语法格式,是用 ** 冒号:** 把元素选择器和伪类名连接起来,中间不能加空格,格式如下:
a:link { 样式声明 }
a:visited { 样式声明 }
a:hover { 样式声明 }
a:active { 样式声明 }
这里给大家补充几个核心的语法细节:
- 元素选择器和伪类名之间,必须用冒号
:连接,中间绝对不能加空格:比如a:hover是正确的,a :hover是错误的,加了空格之后,就变成了后代选择器,选中 a 标签里的:hover 元素,完全不是我们想要的效果,一定要注意。 - 必须按照 LVHA 的顺序书写,绝对不能乱序:这是链接伪类选择器最重要的规则,没有之一。四个伪类选择器,必须按照
a:link→a:visited→a:hover→a:active的顺序书写,简称LVHA 顺序,如果顺序乱了,部分伪类的样式会失效,我们后面会详细讲为什么必须按这个顺序。 - 伪类选择器的冒号,是英文的冒号,绝对不能用中文的冒号:和其他 CSS 语法一样,必须用英文符号,中文冒号会导致样式不生效。
a:hover不仅可以用于<a>标签,还可以用于任何其他的 HTML 元素:比如div:hover、p:hover、.btn:hover,都可以生效,鼠标悬停在这些元素上时,就会触发对应的样式,这是实际开发中非常常用的写法,用来实现元素的悬停交互效果。
3.4.4 完整可运行代码示例:严格按照 LVHA 顺序书写
我们给大家准备了一个完整的、可直接运行的链接伪类选择器示例,严格按照 LVHA 顺序书写,大家可以亲手运行,体验链接的四个不同状态的效果。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>链接伪类选择器示例(严格LVHA顺序)</title>
<style>
/* 给a标签设置通用的基础样式 */
a {
/* 把a标签变成块级元素,每个链接占一行,方便演示 */
display: block;
font-size: 30px;
line-height: 1.5;
margin: 20px 0;
font-family: "微软雅黑", sans-serif;
}
/* 链接伪类选择器:必须严格按照LVHA的顺序书写!
L: link(未访问)
V: visited(已访问)
H: hover(悬停)
A: active(按下)
记忆口诀:"绿化"(Lv绿,Ha化),非常好记
*/
/* 1. a:link:未被访问过的链接,黑色文字,去掉下划线 */
a:link {
color: black;
text-decoration: none;
}
/* 2. a:visited:已经被访问过的链接,绿色文字 */
a:visited {
color: green;
}
/* 3. a:hover:鼠标悬停在链接上时,红色文字,加粗,加下划线 */
a:hover {
color: red;
font-weight: bold;
text-decoration: underline;
}
/* 4. a:active:鼠标按下链接、未松开时,蓝色文字 */
a:active {
color: blue;
}
</style>
</head>
<body>
<!-- 这个a标签,会触发四个不同的状态,大家可以亲手体验一下 -->
<a href="https://www.baidu.com" target="_blank">点击我测试链接伪类(跳转到百度,新窗口打开)</a>
</body>
</html>
大家把这段代码用浏览器打开,就能完整体验链接的四个状态的效果:
- 未访问状态 :刚打开页面的时候,链接是黑色的,没有下划线,
a:link生效; - 鼠标悬停状态 :把鼠标移到链接上,不要点击,链接会变成红色、加粗、带下划线,
a:hover生效; - 鼠标按下状态 :按住鼠标左键,不要松开,链接会变成蓝色,
a:active生效; - 已访问状态 :松开鼠标,跳转到百度,然后回到这个页面,链接会变成绿色,
a:visited生效。
如果大家想让已经访问过的链接,恢复成未访问的状态,只需要清空浏览器的历史记录 就行,Windows 系统可以按Ctrl + Shift + Delete,Mac 系统可以按Command + Shift + Delete,快速打开清除历史记录的面板。
3.4.5 为什么必须严格按照 LVHA 顺序书写?底层原理彻底讲透
很多新手会问:"为什么必须按 LVHA 的顺序写?我乱序写会怎么样?",这里我们用 CSS 的层叠性,给大家彻底讲透底层原理,让大家不仅知其然,更知其所以然,不用死记硬背,也能记住这个顺序。
首先,我们要明确两个核心前提:
- 这四个伪类选择器的优先级,是完全相同的:它们都是 "标签选择器 + 伪类选择器",优先级完全一样。
- 当多个优先级相同的样式,同时作用于同一个元素时,写在后面的样式,会覆盖写在前面的同属性样式:这是 CSS 层叠性的核心规则,我们在之前的文章里已经详细讲过。
接下来,我们来分析用户和链接交互的过程中,一个链接会同时匹配哪几个伪类选择器:
场景 1:一个未被访问过的链接,鼠标悬停在上面
这个时候,这个链接同时匹配两个伪类选择器:a:link(未访问)和a:hover(悬停),两个选择器的优先级相同,哪个写在后面,哪个的样式就会生效。
如果我们把a:hover写在a:link的前面,会发生什么?
/* 错误顺序:hover写在link前面 */
a:hover {
color: red;
}
a:link {
color: black;
}
这个时候,a:link写在后面,会覆盖a:hover的color属性,哪怕鼠标悬停在链接上,文字还是黑色的,a:hover的样式完全失效了,这就是我们不想要的结果。
如果我们把a:hover写在a:link的后面,就不会有这个问题:
/* 正确顺序:link写在前面,hover写在后面 */
a:link {
color: black;
}
a:hover {
color: red;
}
这个时候,鼠标悬停时,a:hover写在后面,覆盖a:link的color属性,文字变成红色,效果正常。
场景 2:一个已被访问过的链接,鼠标悬停在上面
这个时候,这个链接同时匹配两个伪类选择器:a:visited(已访问)和a:hover(悬停),两个选择器的优先级相同,哪个写在后面,哪个的样式就会生效。
和上面的场景一样,如果把a:hover写在a:visited的前面,a:visited会覆盖a:hover的样式,鼠标悬停时,样式不会变化,a:hover失效。所以,a:hover必须写在a:visited的后面。
场景 3:一个链接,鼠标按下了,还没松开
这个时候,这个链接同时匹配三个伪类选择器:如果是未访问的,匹配a:link、a:hover、a:active;如果是已访问的,匹配a:visited、a:hover、a:active。三个选择器的优先级相同,哪个写在后面,哪个的样式就会生效。
我们想要的效果是:鼠标按下的时候,显示a:active的样式,所以a:active必须写在最后面,才能覆盖前面的a:hover的样式。如果把a:active写在a:hover的前面,a:hover会覆盖a:active的样式,鼠标按下的时候,不会显示a:active的样式,完全失效。
最终的正确顺序
通过上面的三个场景分析,我们可以得出,唯一能让四个伪类都正常生效的顺序,就是:a:link → a:visited → a:hover → a:active,也就是 LVHA 顺序。
现在大家明白了,这个顺序不是随便定的,而是由 CSS 的层叠性决定的,顺序乱了,就会导致部分伪类的样式被覆盖,完全失效。大家不用死记硬背,只要理解了层叠性的原理,自然就能记住这个顺序,再配合 "绿化"(Lv Ha)的口诀,就永远不会写错了。
3.4.6 实际开发中的常用简化写法
在实际的企业级开发中,我们很少把四个伪类都写全,因为a:link和a:visited的样式,通常是一样的,a:active的状态非常短暂,用户几乎注意不到,用得很少。
所以,实际开发中,最常用的简化写法是:直接给a标签设置通用样式(包括未访问和已访问的状态),只给a:hover设置鼠标悬停的样式,代码如下:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>实际开发常用的链接伪类简化写法</title>
<style>
/* 实际开发常用简化写法:
1. 直接给a标签设置通用样式,覆盖未访问和已访问的状态
*/
a {
display: block;
font-size: 30px;
line-height: 1.5;
margin: 20px 0;
font-family: "微软雅黑", sans-serif;
/* 未访问和已访问的链接,都是黑色,无下划线 */
color: black;
text-decoration: none;
}
/* 2. 只给a:hover设置鼠标悬停时的样式:红色、加粗、下划线 */
a:hover {
color: red;
font-weight: bold;
text-decoration: underline;
}
</style>
</head>
<body>
<a href="https://www.baidu.com" target="_blank">点击我测试简化写法(跳转到百度)</a>
</body>
</html>
大家运行这段代码就能看到,未访问和已访问的链接,都是黑色无下划线,鼠标悬停时,变成红色、加粗、带下划线,完全满足实际开发的需求,代码更简洁,这是现在企业级开发里最主流的写法。
3.4.7 a:hover的进阶用法:给任何元素设置悬停效果
前面我们提到过,a:hover不仅可以用于<a>标签,还可以用于任何其他的 HTML 元素,比如<div>、<p>、<button>、.btn、.card等等,这是实际开发中非常常用的写法,用来实现元素的悬停交互效果,大幅提升页面的体验。
我们给大家准备了几个非常经典的hover效果示例,都是实际开发中每天都会用到的:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>hover进阶用法:给任意元素设置悬停效果</title>
<style>
/* 全局重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
padding: 50px;
font-family: "微软雅黑", sans-serif;
}
/* 按钮基础样式 */
.btn {
display: inline-block;
padding: 8px 24px;
border-radius: 4px;
background-color: #1677ff;
color: #fff;
text-decoration: none;
font-size: 16px;
line-height: 1.5;
cursor: pointer;
/* 过渡效果,让悬停的变化更流畅,入门阶段了解即可 */
transition: all 0.3s;
margin-right: 20px;
margin-bottom: 30px;
}
/* 按钮悬停效果:背景色变深,稍微放大一点 */
.btn:hover {
background-color: #4096ff;
transform: scale(1.05);
}
/* 卡片基础样式 */
.card {
width: 300px;
border: 1px solid #d9d9d9;
border-radius: 8px;
padding: 20px;
transition: all 0.3s;
}
.card h3 {
font-size: 20px;
color: #333;
margin-bottom: 10px;
transition: all 0.3s;
}
.card p {
font-size: 14px;
color: #666;
line-height: 1.5;
}
/* 卡片悬停效果:向上浮动,加阴影,标题变色 */
.card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
border-color: #1677ff;
}
.card:hover h3 {
color: #1677ff;
}
/* 段落悬停效果:背景变色 */
p {
font-size: 16px;
line-height: 1.5;
padding: 10px;
border-radius: 4px;
transition: all 0.3s;
margin-top: 30px;
}
p:hover {
background-color: #f5f5f5;
}
</style>
</head>
<body>
<!-- 按钮悬停效果 -->
<a href="#" class="btn">按钮1</a>
<button class="btn">按钮2</button>
<!-- 卡片悬停效果 -->
<div class="card">
<h3>卡片标题</h3>
<p>这是卡片的内容,鼠标悬停在卡片上时,卡片会向上浮动,加阴影,标题会变色。</p>
</div>
<!-- 段落悬停效果 -->
<p>这是一段普通的段落,鼠标悬停在上面时,背景会变成浅灰色。</p>
</body>
</html>
大家运行这段代码,就能看到非常丝滑的悬停交互效果:
- 鼠标悬停在按钮上,按钮的背景色会变浅,还会稍微放大一点;
- 鼠标悬停在卡片上,卡片会向上浮动,加上阴影,边框和标题都会变成蓝色;
- 鼠标悬停在段落上,段落的背景会变成浅灰色。
这些效果,都是用:hover伪类实现的,不用写一行 JavaScript 代码,就能实现非常好的交互体验,这是:hover伪类最强大的地方,也是实际开发中用得最多的伪类,没有之一。
3.4.8 作用特点:让静态页面拥有交互反馈,大幅提升用户体验
优点 1:可以给元素的不同状态设置不同的样式,实现基础的交互反馈
这是伪类选择器最大的优点,它能让我们的页面,从静态的、没有反馈的页面,变成动态的、有交互反馈的页面,用户能清晰地知道自己的操作有没有生效,大幅提升用户体验。
优点 2:不用写 JavaScript 代码,只用 CSS 就能实现简单的交互效果
比如悬停变色、放大、加阴影、显示隐藏内容这些简单的交互效果,只用:hover伪类就能实现,不用写一行 JavaScript 代码,开发成本极低,效率极高。
优点 3:写法简单,学习成本低,浏览器兼容性极好
链接伪类选择器的写法非常简单,学习成本极低,而且所有的浏览器,包括非常老的 IE 浏览器,都完美支持,没有任何兼容性问题,所有的网站都可以放心使用。
缺点:只有一个需要注意的点,就是必须按照 LVHA 顺序书写,不然会导致样式失效,这不是选择器本身的缺点,只是写法上的规则。
3.4.9 适用场景:所有需要交互反馈的元素
伪类选择器的适用场景极其广泛,只要是需要给用户交互反馈的元素,都可以用:
- 超链接的状态样式:给链接的不同状态设置不同的样式,给用户清晰的反馈。
- 按钮的悬停、点击效果:给按钮设置鼠标悬停、按下的样式,提升按钮的交互体验,这是最常用的场景。
- 卡片、列表项的悬停效果:给卡片、列表项设置悬停时的阴影、上浮、变色效果,提升页面的质感和体验。
- 导航菜单的悬停、选中效果:给导航菜单项设置悬停、当前选中的样式,让用户清晰地知道自己在哪个页面。
- 表单元素的状态样式:给输入框的获得焦点、禁用、必填状态设置不同的样式,给用户清晰的反馈,这是进阶的伪类选择器的用法,入门阶段了解即可。
3.4.10 新手必踩坑指南:这些错误 90% 的新手都犯过
- 没有按照 LVHA 顺序书写,导致部分伪类样式失效 :这是新手最容易犯的错误,把顺序写乱了,比如把
a:hover写在a:link和a:visited的前面,导致悬停样式不生效,一定要严格按照 LVHA 顺序书写,记住 "绿化" 的口诀。 - 元素选择器和伪类名之间,加了空格 :比如把
a:hover写成了a :hover,加了空格之后,变成了后代选择器,选中 a 标签里的:hover 元素,完全不生效,一定要记住,冒号前后不能加空格。 - 用了中文的冒号,导致样式不生效:把英文的冒号写成了中文的冒号,浏览器解析失败,样式完全不生效,写的时候一定要注意输入法的状态。
- 以为
:hover只能用于 a 标签,不能用于其他元素 :很多新手刚学的时候,以为:hover只能给 a 标签用,其实它可以给任何 HTML 元素用,包括 div、p、button、img 等等,灵活度极高。 a:link只对有 href 属性的 a 标签生效 :如果你的 a 标签没有写 href 属性,比如<a>测试</a>,a:link的样式是不会生效的,只有a的通用样式会生效,一定要给 a 标签加上 href 属性,哪怕是href="#"。a:visited里的样式限制 :很多新手会给a:visited设置背景图片、背景色、字体大小等样式,结果发现不生效。这是因为浏览器为了保护用户的隐私,限制了a:visited能设置的样式,只能修改颜色相关的属性(color、background-color、border-color 等),其他的属性都不能修改,这是浏览器的安全限制,不是你的代码写错了。a:active的触发条件搞混了 :很多新手以为只要按下鼠标,a:active就会生效,其实不是,必须是鼠标按下,并且在链接的范围内松开,才会完整触发。如果鼠标按下之后,移开了链接的范围再松开,a:active就不会生效,这是正常的浏览器行为。
3.5 复合选择器终极小结:一张表记住所有核心知识点
到这里,我们已经把 4 种复合选择器,用保姆级的细节全部讲透了,我们给大家做了一张终极的小结对比表,把所有核心知识点都汇总起来,新手可以直接对照着看,一目了然:
| 选择器类型 | 语法格式 | 核心作用 | 核心特点 | 核心适用场景 | 记忆要点 |
|---|---|---|---|---|---|
| 后代选择器 | 父元素 后代元素(空格分隔) |
选中父元素里的所有后代元素,不管嵌套多少层 | 无层数限制,灵活度极高,用得最多 | 选中某个容器里的所有同名元素,限定样式范围 | 空格分隔,找所有后代,包括儿子、孙子 |
| 子选择器 | 父元素 > 子元素(大于号分隔) |
只选中父元素的直接子元素,不选孙子、重孙子 | 精准度极高,样式隔离性好 | 导航栏一级菜单、只控制直接子元素的样式 | 大于号分隔,只找亲儿子,不找孙子 |
| 并集选择器 | 选择器1, 选择器2, 选择器3(逗号分隔) |
给多个不同的选择器设置相同的样式,简化代码 | 消除代码冗余,降低维护成本 | 提取公共样式,CSS 重置,只要有重复样式就用 | 逗号分隔,集体声明,相同样式只写一遍 |
| 链接伪类选择器 | a:link、a:visited、a:hover、a:active |
给链接的不同状态设置样式,实现交互反馈 | 让静态页面拥有交互效果,不用 JS | 链接、按钮、卡片的悬停交互效果 | 严格按 LVHA 顺序书写,记忆口诀 "绿化" |
四、CSS 选择器优先级终极详解:彻底解决 "样式不生效" 的世纪难题
很多新手学习 CSS 的过程中,遇到最多的问题就是:"我明明写了 CSS 样式,为什么浏览器里不生效?",90% 的情况,都是因为选择器的优先级不够,你写的样式被其他优先级更高的样式覆盖了。
所以,想要彻底解决 "样式不生效" 的问题,就必须彻底搞懂 CSS 选择器的优先级规则,知道哪个选择器的
优先级更高,哪个选择器的样式会最终生效。
这部分内容是 CSS 入门阶段的核心难点之一,很多新手学完选择器之后,还是会被优先级搞得晕头转向,这篇文章里,我会用最通俗易懂的大白话、最完整的代码示例、最细致的底层原理拆解,把 CSS 选择器的优先级彻底讲透,让你以后再也不会遇到 "样式不生效" 的问题。
4.1 优先级的核心定义:为什么会有优先级?
在正式开始讲解优先级规则之前,我们先搞懂一个最核心的问题:为什么 CSS 要有优先级?没有优先级不行吗?
答案是:不行。因为在实际开发中,同一个 HTML 元素,经常会被多个不同的选择器同时选中,并且这些选择器都给这个元素设置了相同的 CSS 属性。
比如,页面里有一个这样的 p 标签:
<p class="red" id="ha">这是一段测试文字</p>
同时,我们有这样的 CSS 代码:
/* 标签选择器 */
p {
color: black;
}
/* 类选择器 */
.red {
color: red;
}
/* id选择器 */
#ha {
color: blue;
}
你看,这个 p 标签,同时被标签选择器 p、类选择器 .red、id 选择器 #ha 选中了,并且这三个选择器都给它设置了 color 属性,分别是黑色、红色、蓝色。
这时候,浏览器就面临一个选择:我到底应该听谁的?到底应该把文字显示成什么颜色?
如果没有优先级规则,浏览器就会乱套,不知道该用哪个样式。所以,CSS 官方制定了一套完整的优先级规则,用来告诉浏览器:当多个选择器同时作用于同一个元素,并且设置了相同的属性时,哪个选择器的优先级更高,就听哪个的。
这就是 CSS 优先级的核心作用:解决多个选择器之间的样式冲突,决定哪个样式最终生效。
4.2 优先级的核心层级(从高到低):一张图记住所有层级
CSS 选择器的优先级,是有明确的高低之分的,我们可以把它分成 7 个核心层级,从最高到最低依次排列,新手只要记住这个顺序,就能解决 99% 的优先级问题。
为了让大家更直观地记住,我给大家做了一个生活化的类比,把每个层级比作不同级别的人物,级别越高,说话越算数:
| 优先级层级(从高到低) | 对应的选择器 / 样式类型 | 生活化类比(级别从高到低) | 代码示例 |
|---|---|---|---|
| 1(最高,绝对不要用) | !important 关键字 |
皇帝,说的话是圣旨,没人敢不听 | color: red !important; |
| 2 | 行内样式(写在 HTML 标签的 style 属性里) |
宰相,一人之下,万人之上 | <p style="color: red;">文字</p> |
| 3 | id 选择器 | 一品大臣 | #ha { color: red; } |
| 4 | 类选择器、伪类选择器、属性选择器 | 二品大臣 | .red { color: red; }``a:hover { color: red; }``[type="text"] { color: red; } |
| 5 | 标签选择器 | 七品县令 | p { color: red; } |
| 6 | 通配符选择器 | 普通老百姓 | * { color: red; } |
| 7(最低) | 继承的样式(从父元素那里继承来的) | 空气,几乎没有存在感 | body { color: red; }(p 标签继承 body 的颜色) |
这个顺序非常重要,新手一定要刻在脑子里:!important > 行内样式 > id 选择器 > 类 / 伪类 / 属性选择器 > 标签选择器 > 通配符选择器 > 继承的样式。
接下来,我们一个一个层级给大家详细讲解,每个层级都有完整的代码示例,让大家亲眼看到优先级的效果。
4.3 每个优先级层级的详细解析:从最高到最低,逐个攻破
4.3.1 第 1 层级:!important 关键字(优先级最高,新手绝对不要用!)
!important 是 CSS 里的一个特殊关键字,它可以把某个样式的优先级,提升到最高的级别,没有任何选择器能覆盖它,哪怕是行内样式也不行。
语法格式
!important 要写在样式属性值的后面,分号的前面,中间用空格隔开:
选择器 {
属性名: 属性值 !important;
}
完整代码示例
我们给大家一个完整的代码示例,看看 !important 有多霸道:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>!important 示例(新手绝对不要用!)</title>
<style>
/* 通配符选择器,加了!important */
* {
color: pink !important;
}
/* 标签选择器 */
p {
color: black;
}
/* 类选择器 */
.red {
color: red;
}
/* id选择器 */
#ha {
color: blue;
}
</style>
</head>
<body>
<!-- 这个p标签,同时被通配符、标签、类、id选择器选中,并且通配符选择器加了!important -->
<p class="red" id="ha" style="color: green;">
测试文字(最终是粉色,因为!important优先级最高)
</p>
</body>
</html>
大家运行这段代码就能看到,哪怕我们给 p 标签加了行内样式 color: green;,加了 id 选择器 #ha { color: blue; },加了类选择器 .red { color: red; },加了标签选择器 p { color: black; },最终文字的颜色还是粉色的 ------ 因为通配符选择器里加了 !important,它的优先级是最高的,没有任何选择器能覆盖它。
为什么新手绝对不要用 !important?
虽然 !important 很强大,但是新手绝对不要用它,哪怕你的样式死活不生效,也不要用,原因有三个:
- 它会彻底破坏 CSS 的层叠性,让代码变得完全无法维护 CSS 的核心设计理念就是层叠性,通过优先级规则来自然地解决样式冲突。如果你随便用
!important,就相当于用皇帝的圣旨去强制覆盖所有的规则,后面你想要修改这个样式的时候,只能用另一个!important去覆盖它,最后你的 CSS 代码里会到处都是!important,变成一团乱麻,哪怕是你自己,过一个月也看不懂,更别说其他开发者了。 - 它会让你无法真正理解 CSS 的优先级规则 很多新手遇到样式不生效的问题,就直接加
!important,虽然样式生效了,但是你永远不知道为什么之前的样式不生效,永远学不会 CSS 的优先级规则,永远无法真正掌握 CSS。 - 它会导致很多意外的样式问题 如果你给一个通用的样式加了
!important,比如* { color: red !important; },那么页面里所有的元素的文字颜色都会变成红色,哪怕你后面用 id 选择器、行内样式去修改,也改不了,会导致整个页面的样式都乱套,很难修复。
哪怕你的样式死活不生效,也不要用 !important,先去检查是不是选择器写错了,是不是优先级不够,是不是有其他样式覆盖了,用正确的方法去解决问题,而不是用 !important 去暴力覆盖。
4.3.2 第 2 层级:行内样式(优先级第二高,尽量少用)
行内样式,就是写在 HTML 标签的 style 属性里的样式,它的优先级仅次于 !important,比所有的 CSS 选择器都高。
语法格式
行内样式直接写在 HTML 标签的 style 属性里,多个样式属性之间用分号隔开:
<标签名 style="属性名1: 属性值1; 属性名2: 属性值2;">内容</标签名>
完整代码示例
我们给大家一个完整的代码示例,看看行内样式的优先级:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>行内样式优先级示例</title>
<style>
/* id选择器 */
#ha {
color: blue;
font-size: 20px;
}
/* 类选择器 */
.red {
color: red;
font-size: 30px;
}
/* 标签选择器 */
p {
color: black;
font-size: 40px;
}
</style>
</head>
<body>
<!-- 这个p标签,有行内样式 style="color: green; font-size: 50px;" -->
<p class="red" id="ha" style="color: green; font-size: 50px;">
测试文字(最终是绿色、50px,因为行内样式优先级最高)
</p>
</body>
</html>
大家运行这段代码就能看到,最终文字的颜色是绿色的,大小是 50px------ 因为行内样式的优先级比 id 选择器、类选择器、标签选择器都高,它会覆盖这些选择器里的同属性样式。
为什么尽量少用行内样式?
虽然行内样式的优先级很高,但是我们尽量不要用它,原因有三个:
- 它违反了 CSS 的核心设计理念:结构与样式分离 HTML 的作用是定义页面的结构(比如哪里是标题,哪里是段落,哪里是图片),CSS 的作用是定义页面的样式(比如标题是什么颜色,段落是什么大小)。如果你把样式写在 HTML 标签的
style属性里,就把结构和样式混在一起了,后期维护的时候,你要修改样式,就得去改 HTML 代码,非常麻烦,也不符合前端开发的规范。 - 它的代码复用性为 0行内样式只能作用于当前这一个标签,如果你有 10 个标签都要用同样的样式,你就得给 10 个标签都写一遍一模一样的行内样式,代码冗余度极高,完全没有复用性。
- 它的优先级太高,后期很难覆盖 行内样式的优先级仅次于
!important,如果你给某个元素写了行内样式,后期想要用 CSS 选择器去修改它的样式,就必须用!important,不然覆盖不了,会导致代码变得很难维护。
新手记住:尽量不要用行内样式,所有的样式都尽量写在 CSS 里(内部样式或外部样式),用选择器去选中元素,实现结构与样式的彻底分离。
4.3.3 第 3 层级:id 选择器(优先级第三高)
id 选择器的优先级,排在第 3 位,比类选择器、标签选择器都高,仅次于 !important 和行内样式。
完整代码示例
我们给大家一个完整的代码示例,看看 id 选择器的优先级:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>id选择器优先级示例</title>
<style>
/* 类选择器 */
.red {
color: red;
font-size: 30px;
}
/* 标签选择器 */
p {
color: black;
font-size: 40px;
}
/* id选择器,优先级比类和标签都高 */
#ha {
color: blue;
font-size: 20px;
}
</style>
</head>
<body>
<!-- 这个p标签,同时被标签、类、id选择器选中 -->
<p class="red" id="ha">
测试文字(最终是蓝色、20px,因为id选择器优先级最高)
</p>
</body>
</html>
大家运行这段代码就能看到,最终文字的颜色是蓝色的,大小是 20px------ 因为 id 选择器的优先级比类选择器和标签选择器都高,它会覆盖这两个选择器里的同属性样式。
4.3.4 第 4 层级:类选择器、伪类选择器、属性选择器(优先级第四高)
这三个选择器的优先级是一样的,排在第 4 位,比标签选择器高,比 id 选择器低。
- 类选择器:
.red、.btn、.box - 伪类选择器:
a:hover、a:link、:focus - 属性选择器:
[type="text"]、[href^="https"]
完整代码示例
我们给大家一个完整的代码示例,看看这三个选择器的优先级:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>类/伪类/属性选择器优先级示例</title>
<style>
/* 标签选择器 */
p {
color: black;
font-size: 40px;
}
/* 类选择器 */
.red {
color: red;
font-size: 30px;
}
/* 伪类选择器:hover,和类选择器优先级相同,写在后面,覆盖类选择器的color */
p:hover {
color: green;
}
/* 属性选择器:和类选择器优先级相同,写在最后,覆盖前面的font-size */
[data-test] {
font-size: 20px;
}
</style>
</head>
<body>
<!-- 这个p标签,同时被标签、类、伪类、属性选择器选中 -->
<p class="red" data-test="1">
测试文字(默认是红色、20px;鼠标悬停时是绿色、20px)
</p>
</body>
</html>
大家运行这段代码就能看到:
- 默认状态下,文字是红色的、20px------ 因为类选择器
.red和属性选择器[data-test]的优先级相同,属性选择器写在后面,覆盖了类选择器的font-size; - 鼠标悬停时,文字变成绿色的、20px------ 因为伪类选择器
p:hover和类选择器优先级相同,写在后面,覆盖了类选择器的color。
4.3.5 第 5 层级:标签选择器(优先级第五高)
标签选择器的优先级,排在第 5 位,比通配符选择器高,比类、伪类、属性、id 选择器都低。
完整代码示例
我们给大家一个完整的代码示例,看看标签选择器的优先级:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>标签选择器优先级示例</title>
<style>
/* 通配符选择器 */
* {
color: pink;
font-size: 50px;
}
/* 标签选择器,优先级比通配符高 */
p {
color: black;
font-size: 40px;
}
</style>
</head>
<body>
<!-- 这个p标签,同时被通配符和标签选择器选中 -->
<p>
测试文字(最终是黑色、40px,因为标签选择器优先级比通配符高)
</p>
</body>
</html>
大家运行这段代码就能看到,最终文字的颜色是黑色的,大小是 40px------ 因为标签选择器的优先级比通配符选择器高,它会覆盖通配符选择器里的同属性样式。
4.3.6 第 6 层级:通配符选择器(优先级第六高)
通配符选择器的优先级,排在第 6 位,只比继承的样式高,比其他所有选择器都低。
4.3.7 第 7 层级:继承的样式(优先级最低,几乎没有存在感)
继承的样式,就是元素从它的父元素、祖先元素那里继承来的样式,它的优先级是最低的,任何选择器的样式,都能覆盖继承的样式。
不是所有的 CSS 属性都能继承,只有和文字相关的属性才能继承,比如 color、font-size、font-family、line-height、text-align 等;和布局、盒子模型相关的属性,都不能继承,比如 width、height、margin、padding、border、background-color 等。
完整代码示例
我们给大家一个完整的代码示例,看看继承的样式的优先级:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>继承的样式优先级示例</title>
<style>
/* body的样式,p标签会继承color和font-size */
body {
color: pink;
font-size: 50px;
}
/* 通配符选择器,优先级比继承的样式高 */
* {
color: black;
}
/* 标签选择器,优先级比通配符高 */
p {
font-size: 40px;
}
</style>
</head>
<body>
<!-- 这个p标签,继承了body的color和font-size,同时被通配符和标签选择器选中 -->
<p>
测试文字(最终是黑色、40px,因为通配符和标签选择器覆盖了继承的样式)
</p>
</body>
</html>
大家运行这段代码就能看到,最终文字的颜色是黑色的,大小是 40px------ 因为通配符选择器覆盖了继承的 color,标签选择器覆盖了继承的 font-size,继承的样式优先级最低,很容易就被覆盖了。
4.4 优先级相同时的规则:层叠性,后面的覆盖前面的
如果多个选择器的优先级完全相同,并且都给同一个元素设置了相同的属性,这时候应该听谁的?
这时候就要用到 CSS 的另一个核心特性:层叠性。
层叠性的核心规则非常简单:当多个优先级相同的选择器,同时作用于同一个元素,并且设置了相同的属性时,写在后面的选择器的样式,会覆盖写在前面的选择器的同属性样式。
简单来说就是:优先级相同,后来者居上。
完整代码示例
我们给大家一个完整的代码示例,看看层叠性的效果:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>层叠性示例:优先级相同,后来者居上</title>
<style>
/* 第一个类选择器 */
.red {
color: red;
font-size: 30px;
}
/* 第二个类选择器,和第一个优先级相同,写在后面,覆盖color属性 */
.blue {
color: blue;
}
/* 第一个标签选择器 */
p {
background-color: #f5f5f5;
padding: 10px;
}
/* 第二个标签选择器,和第一个优先级相同,写在后面,覆盖background-color属性 */
p {
background-color: #e5e5e5;
}
</style>
</head>
<body>
<!-- 这个p标签,同时被两个类选择器和两个标签选择器选中 -->
<p class="red blue">
测试文字(最终是蓝色、30px、浅灰色背景)
</p>
</body>
</html>
大家运行这段代码就能看到:
- 文字颜色是蓝色的:因为
.blue写在.red的后面,覆盖了color属性; - 文字大小是 30px:因为
.blue没有设置font-size,所以没有覆盖,沿用.red的; - 背景颜色是浅灰色
#e5e5e5:因为第二个p标签选择器写在后面,覆盖了background-color属性; - 内边距是 10px:因为第二个
p标签选择器没有设置padding,所以没有覆盖,沿用第一个的。
这就是层叠性的核心规则:优先级相同,后面的覆盖前面的,只覆盖相同的属性,不同的属性会保留下来,叠加生效。
4.5 常见的优先级问题与解决方法
很多新手遇到 "样式不生效" 的问题,都是因为优先级不够,我们给大家总结了 3 个最常见的问题,以及对应的解决方法:
问题 1:我用类选择器设置了样式,为什么不生效?
可能的原因:
- 有 id 选择器、行内样式给同一个元素设置了相同的属性,覆盖了类选择器的样式;
- 有另一个优先级相同的类选择器,写在后面,覆盖了你的类选择器的样式;
- 你的类选择器写错了(比如漏写了
.,类名拼写错误,HTML 里的 class 属性写错了)。
解决方法:
- 打开浏览器的开发者工具(按 F12),切换到
Elements面板,选中那个元素,看右边的Styles面板:- 看看你的类选择器的样式是不是被划掉了,如果被划掉了,说明被更高优先级的样式覆盖了;
- 看看上面是不是有 id 选择器、行内样式的同属性样式;
- 看看是不是有另一个类选择器写在后面,覆盖了你的样式。
- 检查你的类选择器的拼写,确保 CSS 里的类名和 HTML 里的 class 属性完全一致,不要漏写
.。
问题 2:我想覆盖 id 选择器的样式,怎么办?
可能的原因:你用类选择器去覆盖 id 选择器的样式,但是类选择器的优先级比 id 选择器低,覆盖不了。
解决方法:
- 方法 1(推荐):尽量不要用 id 选择器设置样式,把 id 选择器改成类选择器,然后用另一个类选择器去覆盖它;
- 方法 2(不推荐) :如果你必须用 id 选择器,可以写一个更具体的 id 选择器(比如
div#ha),或者写一个优先级相同的 id 选择器,写在后面; - 方法 3(绝对不推荐) :用
!important去覆盖,但是新手千万不要这么做。
问题 3:我想覆盖行内样式的样式,怎么办?
可能的原因:你用 CSS 选择器去覆盖行内样式,但是 CSS 选择器的优先级比行内样式低,覆盖不了。
解决方法:
- 方法 1(推荐):把行内样式删掉,改成用 CSS 选择器设置样式,实现结构与样式分离;
- 方法 2(绝对不推荐) :用
!important去覆盖,但是新手千万不要这么做。
4.6 优先级避坑指南:新手绝对不要踩的 5 个坑
- 绝对不要滥用
!important:哪怕你的样式死活不生效,也不要用!important,先去检查是不是选择器写错了,是不是优先级不够,用正确的方法解决问题。 - 尽量不要用行内样式:所有的样式都尽量写在 CSS 里,用选择器去选中元素,实现结构与样式分离。
- 尽量不要用 id 选择器设置 CSS 样式:id 选择器主要留给 JavaScript 用,CSS 样式优先用类选择器,类选择器更灵活,复用性更高。
- 不要以为选择器写得越长,优先级越高 :很多新手以为
div ul li p .red比.red的优先级高,其实不是,优先级只看选择器的类型,不看选择器的长度。div ul li p .red还是类选择器,优先级和.red一样,只是更具体一点。 - 不要忘记层叠性的规则:优先级相同的时候,后面的覆盖前面的,写代码的时候要注意选择器的顺序。
4.7 优先级终极小结:一张表彻底记住所有规则
为了让大家更清晰地记住优先级的所有规则,我们给大家做了一张终极的小结表:
| 优先级层级(从高到低) | 选择器 / 样式类型 | 示例 | 注意事项 |
|---|---|---|---|
| 1(最高,禁用) | !important |
color: red !important; |
新手绝对不要用,会破坏层叠性 |
| 2(尽量少用) | 行内样式 | <p style="color: red;"> |
尽量不用,违反结构与样式分离 |
| 3(谨慎使用) | id 选择器 | #ha { color: red; } |
优先留给 JavaScript 用,CSS 尽量用类 |
| 4(最常用) | 类选择器、伪类选择器、属性选择器 | .red { color: red; }``a:hover { color: red; } |
优先级适中,灵活度高,推荐优先使用 |
| 5 | 标签选择器 | p { color: red; } |
用于设置全局通用基础样式 |
| 6 | 通配符选择器 | * { color: red; } |
仅用于 CSS 全局重置,清除默认内外边距 |
| 7(最低) | 继承的样式 | body { color: red; }(p 继承) |
优先级最低,很容易被覆盖 |
| 优先级相同 | 层叠性:后面的覆盖前面的 | .red { color: red; }``.blue { color: blue; }(blue 生效) |
只覆盖相同属性,不同属性叠加生效 |
结语
这篇超万字的保姆级全解析,到这里就全部结束了。我们从 CSS 选择器的核心作用开始,用生活化的类比,让大家理解了选择器是 CSS 的 "灵魂"------ 只有先选中元素,才能给元素设置样式。
然后,我们用保姆级的细节,逐个讲解了 4 种基础选择器:
- 标签选择器:给所有同名标签批量设置样式,用于全局通用基础样式;
- 类选择器:CSS 里最重要的选择器,灵活度极高,支持多类名组合,实现样式模块化,90% 的 CSS 样式都用它;
- id 选择器:唯一性最强,主要留给 JavaScript 用,CSS 里尽量少用;
- 通配符选择器:最 "霸道",选中所有元素,仅用于 CSS 全局重置。
接着,我们又逐个讲解了 4 种复合选择器:
- 后代选择器:选中父元素里的所有后代,无层数限制,用得最多;
- 子选择器:只选中父元素的亲儿子,不选孙子,精准度更高;
- 并集选择器:给多个选择器设置相同的样式,简化代码,消除冗余;
- 链接伪类选择器 :给链接的不同状态设置样式,重点掌握
:hover,实现交互反馈。
最后,我们用最细致的底层原理,彻底讲解了 CSS 选择器的优先级规则,以及优先级相同时的层叠性规则,让大家彻底解决 "样式不生效" 的世纪难题。
CSS 选择器是 CSS 的根基,也是前端开发的核心知识点,只有彻底掌握了选择器,才能精准地控制页面里的每一个元素,才能写出好看、美观、易维护的网页。希望这篇文章,能帮你彻底打开 CSS 的大门,从零到一,真正掌握 CSS 选择器,在前端的世界里,创造出更多精彩的页面。