对于CSS:选择器的解析

开篇介绍:

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 种基础选择器,按照使用频率和重要性排序分别是:

  1. 标签选择器(元素选择器):最基础的选择器,用 HTML 标签名直接选择元素;
  2. 类选择器:CSS 里最灵活、最常用、最重要的选择器,没有之一;
  3. id 选择器:唯一性最强的选择器,主要用于 JavaScript 交互,CSS 里要谨慎使用;
  4. 通配符选择器:最 "霸道" 的选择器,会选中页面里的所有元素,有固定的使用场景,不能滥用。

第二大类:复合选择器

复合选择器,就是把两个或多个基础选择器,按照固定的规则组合在一起形成的选择器。如果说基础选择器是单个积木块,那复合选择器就是把积木块搭成的造型,能实现更精准、更灵活、更复杂的元素选择,解决基础选择器解决不了的场景问题。

入门阶段,我们必须 100% 掌握 4 种复合选择器,按照使用频率和重要性排序分别是:

  1. 后代选择器(包含选择器):最常用的复合选择器,用于选中某个父元素里的所有后代元素;
  2. 子选择器:比后代选择器更精准,只选中父元素的亲儿子元素,不选孙子、重孙子;
  3. 并集选择器(集体声明):用于给多个不同的选择器设置相同的样式,大幅简化代码,减少冗余;
  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;
    /* 可以写无数条样式声明 */
}

这里给大家补充几个语法上的底层细节:

  1. 标签名必须和 HTML 里的标签名完全一致:HTML 里的标签是<p>,CSS 里就必须写p,不能写P、paragraph、段落,否则浏览器完全认不出来;
  2. 标签名前面、后面都不能加任何额外的符号:很多新手刚入门,会不小心写成.p或者#p,这两个完全不是标签选择器,.p是类选择器,#p是 id 选择器,和标签选择器p的作用天差地别,一定要注意;
  3. 大括号、冒号、分号必须是英文的 :绝对不能用中文的全角符号,比如中文的冒号:、分号;、大括号{},浏览器是认不出来的,会直接导致样式不生效,这是新手最容易踩的低级错误,没有之一;
  4. 每条样式声明结束后必须加分号:哪怕是最后一条声明,也推荐大家加上分号,避免后面加新的声明时,忘记加分号导致样式失效。
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 浏览器给表单元素设置的默认样式,完全不一样。

这些默认样式,会导致两个问题:

  1. 页面会出现很多我们不想要的默认间距,打乱我们的排版;
  2. 同一个页面,在不同的浏览器里显示的效果不一样,出现兼容性问题。

所以,在实际开发中,我们都会在 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 个坑,每一个都是真实开发中高频出现的错误,大家一定要避开:

  1. 标签名拼写错误 :比如把p写成p1,把div写成deiv,把ul写成ui,浏览器认不出来,样式完全不生效。写的时候一定要仔细检查标签名的拼写,和 HTML 里的标签名完全一致。

  2. 给标签名加了多余的符号 :比如把p写成.p或者#p,这两个完全不是标签选择器,样式自然不会生效。一定要记住,标签选择器直接写标签名,前面不要加任何符号。

  3. 用了中文的标点符号:比如中文的冒号、分号、大括号,浏览器完全认不出来,会导致整个样式块失效。写代码的时候,一定要把输入法切换到英文状态,避免出现这个问题。

  4. 忘记给样式声明加分号 :每条样式声明的结尾必须加分号,如果你忘记加,后面的所有样式声明都会失效。比如:

    复制代码
    p {
        color: red /* 这里忘记加分号,下面的font-size完全不生效 */
        font-size: 30px;
    }
  5. 滥用标签选择器,导致样式污染 :很多新手图省事,不管什么场景都用标签选择器,比如写了div { color: red; },结果页面里所有的 div 都变成了红色,包括你不想改的导航栏、页脚里的 div,导致全页面的样式都乱了,这就是样式污染。一定要记住,标签选择器只能用来设置全局的通用样式,不能用来设置局部的特殊样式。

  6. 标签名大小写混写 :虽然 CSS 不区分大小写,P和p都能选中 p 标签,但是行业规范里必须统一用小写字母,大小写混写会导致代码可读性极差,也容易出现拼写错误。

  7. 用标签选择器设置局部的特殊样式 :比如你只想给页面里某一个 div 设置样式,结果用了div标签选择器,导致全页面的 div 都被改了,这是新手最常见的错误。遇到这种需要差异化选择的场景,一定要用类选择器,绝对不要用标签选择器。

  8. 不理解优先级,标签选择器的样式被覆盖了,还不知道为什么 :很多新手写了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;
    /* 可以写无数条样式声明 */
}

这里给大家补充几个定义类时的底层细节:

  1. 必须以英文句号.开头,绝对不能漏 :这是类选择器的标志,浏览器看到.开头的选择器,就知道这是一个类选择器。如果漏了.,浏览器就会把它当成标签选择器,去找和类名同名的 HTML 标签,自然就找不到元素,样式不生效
  2. 类名是我们自己定义的,必须符合命名规范:类名不能随便乱起,必须符合我们后面会讲的命名规范,不然会出现兼容性问题、可读性问题。
  3. 类名区分大小写 :.Red和.red是两个完全不同的类,浏览器会把它们当成两个不同的选择器,所以我们必须统一用小写字母,避免出现这种问题。
  4. 一个 CSS 文件里,可以定义无数个不同的类:没有数量限制,你可以根据需求,定义任意多个类。
第二部分:在 HTML 里调用类

在 CSS 里定义好类之后,我们需要在 HTML 标签里,用class属性来调用这个类,格式如下:

复制代码
<标签名 class="你在CSS里定义的类名">标签里的内容</标签名>

这里给大家补充几个调用类时的底层细节,新手一定要记牢:

  1. class 属性里的类名,不需要加英文句号. :只需要写类名本身就行,比如 CSS 里定义的是.red,HTML 里就写class="red",绝对不能写class=".red"
  2. 一个标签可以同时调用多个类名 :多个类名之间用空格 分隔,比如class="box red-bg border",这个标签会同时套用.box、.red-bg、.border三个类的所有样式,这是类选择器最强大的特性之一,我们后面会详细讲。
  3. 同一个类名,可以给无数个不同的标签调用 :不管是<p>、<div>、<a>、<button>,还是其他任何标签,都可以调用同一个类名,实现样式的跨标签复用。
  4. 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 个尺寸类,就能组合出各种各样的按钮:主按钮、次要按钮、危险按钮、大按钮、小按钮、大危险按钮、小次要按钮等等,只要你想,就能组合出无数种不同的按钮,完全不用重复写代码。

这种写法的好处,简直是碾压级的:

  1. 代码复用性拉满:公共样式只写一遍,所有按钮都能复用,没有任何冗余代码;
  2. 维护成本极低 :如果后期要修改所有按钮的圆角,只需要修改.btn类里的border-radius这一行代码,所有按钮都会自动更新,不用一个一个修改;如果要修改主按钮的蓝色,只需要修改.btn-primary里的背景色,所有主按钮都会更新,效率极高;
  3. 灵活性极高:我们可以根据需求,自由组合不同的类,想要什么类型的按钮,就组合对应的类,不用修改 CSS 代码,只需要调整 HTML 里的 class 属性就行;
  4. 可读性极强 :看到 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 样式,都是用类选择器来写的,我们给大家总结了它的核心适用场景:

  1. 给页面里的部分元素设置差异化的样式:这是最基础的场景,比如给部分段落设置特殊的颜色、给部分按钮设置特殊的样式。
  2. 提取公共样式,实现代码复用:把页面里重复出现的样式,提取成一个公共的类,所有需要这个样式的元素,都调用这个类,不用重复写代码。
  3. 样式模块化组合,实现复杂的样式需求:把不同的样式拆分成独立的类,自由组合,实现各种复杂的样式效果,比如按钮、卡片、导航栏等组件的样式。
  4. 企业级项目的核心样式写法:所有的企业级前端项目,都是以类选择器为核心来写 CSS 的,不管是原生 CSS,还是 Vue、React 的组件样式,都是优先用类选择器。

简单来说:只要你需要设置 CSS 样式,优先考虑用类选择器,它能解决 99% 的场景问题。

2.2.8 踩坑指南

我们给大家总结了最容易踩的 10 个坑,每一个都是高频出现的错误,大家一定要避开:

  1. CSS 里的类选择器漏写了开头的英文句号. :比如把.red写成了red,浏览器会把它当成标签选择器,去找<red>标签,自然找不到元素,样式完全不生效。这是新手用类选择器时,最容易犯的错误,没有之一,写的时候一定要检查开头有没有.。
  2. HTML 里的 class 属性里,给类名加了英文句号. :比如把class="red"写成了class=".red",浏览器会找类名是.red的类,而你 CSS 里定义的是.red,对应的类名是red,自然匹配不上,样式不生效。一定要记住:CSS 里定义类要加.,HTML 里调用类不要加.。
  3. 一个标签调用多个类名时,类名之间没有用空格分隔,用了逗号或者其他符号 :比如把class="box red-bg"写成了class="box,red-bg"或者class="box.red-bg",浏览器会把box,red-bg当成一个完整的类名,自然找不到对应的类,样式不生效。多个类名之间,必须用空格分隔,绝对不能用其他符号。
  4. 类名拼写错误 :CSS 里定义的是.primary,HTML 里写的是class="primay",少了一个 r,自然匹配不上,样式不生效。写的时候一定要仔细检查类名的拼写,确保 CSS 和 HTML 里的类名完全一致。
  5. 类名大小写混写,CSS 和 HTML 里的大小写不一致 :CSS 里定义的是.Red,HTML 里写的是class="red",类名是区分大小写的,这两个是完全不同的类,自然匹配不上。一定要统一用小写字母写类名,避免出现这个问题。
  6. 多个类的样式冲突了,以为是 HTML 里的类名顺序决定的 :很多新手会问,"我给一个标签同时加了.red和.blue两个类,都设置了 color 属性,为什么最终是蓝色,不是红色?是不是因为 HTML 里class="red blue",blue 写在后面?"。这里给大家明确:多个类名的样式冲突时,最终生效的样式,和 HTML 里的类名顺序完全无关,只和 CSS 里的书写顺序有关。CSS 里写在后面的类,会覆盖前面的类的同属性样式,和 HTML 里的顺序没有任何关系。
  7. 类名命名不规范,用中文、拼音、纯数字、无意义的字母:这个我们前面已经讲过了,不规范的类名,会导致代码可读性极差,维护成本极高,甚至会出现样式不生效的问题,一定要遵守命名规范。
  8. 根据样式命名类名,导致后期维护灾难 :比如定义了.red类,后期要改成蓝色,代码里的类名和实际样式完全不符,维护起来极其麻烦,一定要根据元素的用途命名,不要根据样式命名。
  9. 滥用类选择器,给每个元素都加一堆无意义的类:很多新手学了类选择器之后,不管什么元素都加类,哪怕是页面里唯一的一个元素,也要加个类,甚至给每个元素都加十几个类,导致 HTML 代码极其臃肿,可读性极差。类选择器要合理使用,公共样式提取成类,特殊样式加类,不要滥用。
  10. 不理解优先级,类选择器的样式被 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;
    /* 可以写无数条样式声明 */
}

这里的核心细节:

  1. 必须以井号#开头,绝对不能漏 :这是 id 选择器的标志,浏览器看到#开头的选择器,就知道这是一个 id 选择器。如果漏了#,浏览器会把它当成标签选择器,样式不生效。
  2. id 名的命名规范,和类名完全一致 :必须用有意义的英文单词,多个单词用连字符-分隔,以英文字母开头,不能用中文、纯数字、保留字,统一用小写字母,区分大小写。
  3. id 名是唯一的,一个 CSS 文件里,同一个 id 名只能定义一次:虽然语法上允许你定义多次,但是根据唯一性原则,不推荐这么做。
第二部分:在 HTML 里调用 id 选择器

在 CSS 里定义好 id 之后,我们需要在 HTML 标签里,用id属性来调用这个 id,格式如下:

复制代码
<标签名 id="你在CSS里定义的id名">标签里的内容</标签名>

这里的核心细节,也是 id 选择器最重要的规则,没有之一:

  1. id 属性里的 id 名,不需要加井号# :只需要写 id 名本身就行,比如 CSS 里定义的是#ha,HTML 里就写id="ha",绝对不能写id="#ha",这是新手容易踩的坑。
  2. 同一个 HTML 页面里,同一个 id 名只能给一个标签使用,绝对不能重复! :这是 id 选择器的铁律,是 HTML 官方规范里明确规定的,没有任何例外。比如你给第一个 div 加了id="box",页面里其他所有的标签,都不能再用id="box"了,哪怕是不同名的标签也不行。
  3. 一个标签只能有一个 id 属性,只能写一个 id 名,绝对不能同时写多个 id :比如id="box header"是完全错误的,一个标签只能有一个唯一的 id,不能同时有多个 id,这和类选择器完全不同,一定要注意。
  4. 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 重复做了兼容处理。但是,这绝对不代表你可以这么做,因为:

  1. 这是完全违反 HTML 官方规范的,W3C 的 HTML 标准里,明确规定了 id 属性在整个文档中必须是唯一的,不符合规范的代码,就是不专业的、错误的代码。
  2. JavaScript 里,id 重复会导致致命的 bug :JavaScript 里有一个原生方法document.getElementById(),就是通过 id 来获取元素的,这个方法只会返回页面里第一个带这个 id 的元素,后面重复的 id 的元素,完全获取不到。比如上面的示例里,你用document.getElementById("box"),只会拿到第一个 div,拿不到第二个 div 和 p 标签,后续的所有交互操作都会失效,这是致命的 bug。
  3. 锚点跳转功能会失效 :HTML 里可以通过 id 实现页面内的锚点跳转,比如<a href="#box">跳转到box</a>,如果页面里有多个id="box",点击这个链接,只会跳到第一个带id="box"的元素,后面的都跳不到。
  4. SEO 会受到影响:搜索引擎的爬虫,会根据 id 来识别页面的结构,重复的 id 会导致爬虫无法正确解析页面,影响页面的搜索排名。
  5. 无障碍访问会失效:屏幕阅读器等无障碍工具,是通过 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 选择器的特点,我们给大家明确了它的核心适用场景,以及不推荐的场景:

推荐的适用场景
  1. JavaScript 获取元素,实现页面交互效果 :这是 id 选择器现在最主要、最核心的用途,没有之一。比如你要给一个按钮加点击事件,给一个弹窗加显示隐藏的效果,给一个表单加验证逻辑,都可以给这个元素加一个唯一的 id,然后用document.getElementById()获取到这个元素,再写交互逻辑。
  2. 页面内的锚点跳转 :比如页面顶部的导航栏,点击 "联系我们",直接跳转到页面底部的联系我们板块,就可以给底部的板块加一个id="contact",然后导航栏的链接写<a href="#contact">联系我们</a>,点击就能直接跳转到对应的位置,这是 HTML 里非常常用的锚点跳转功能。
  3. 给页面里唯一的、不会复用的元素设置样式:比如页面里唯一的页面根容器、唯一的头部、唯一的页脚,这些元素在页面里只会出现一次,不会复用,也可以用 id 选择器来设置样式,但是更推荐用类选择器。
绝对不推荐的场景
  1. 给需要复用的元素设置样式:比如按钮、卡片、列表项这些会重复出现的元素,绝对不要用 id 选择器,必须用类选择器。
  2. 大型项目里的通用样式:大型项目里,绝对不要到处用 id 选择器设置样式,会导致优先级混乱,维护成本极高,必须用类选择器。

这里给新手一个明确的行业通用规范:CSS 样式优先用类选择器来写,id 选择器主要留给 JavaScript 用,尽量不要用 id 选择器来设置 CSS 样式。这是现在所有前端团队都遵守的通用规范,新手从一开始就要养成这个习惯。

2.3.8 踩坑指南
  1. 同一个 HTML 页面里,重复使用同一个 id:这是最严重、最致命的错误,哪怕 CSS 样式能生效,也绝对不能这么做,会导致 JavaScript、锚点跳转、SEO、无障碍访问都出现问题,一定要严格遵守 id 的唯一性规则。
  2. CSS 里的 id 选择器漏写了开头的井号# :比如把#ha写成了ha,浏览器会把它当成标签选择器,样式不生效,一定要检查开头的#。
  3. HTML 里的 id 属性里,给 id 名加了井号# :比如把id="ha"写成了id="#ha",匹配不上 CSS 里的 id 选择器,样式不生效,一定要记住:CSS 里定义 id 要加#,HTML 里调用 id 不要加#。
  4. 一个标签同时写了多个 id :比如id="box header",这是完全错误的,一个标签只能有一个 id,不能同时写多个,和类选择器完全不同,一定要注意。
  5. 滥用 id 选择器来设置 CSS 样式:不管什么元素都加 id,用 id 选择器来写样式,导致代码冗余、优先级混乱、无法维护,一定要记住,CSS 样式优先用类选择器,id 主要留给 JavaScript 用。
  6. id 名拼写错误、大小写不一致:和类选择器一样,id 名也是区分大小写的,CSS 里的 id 名和 HTML 里的 id 名必须完全一致,不然匹配不上,样式不生效。
  7. 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 踩坑指南
  1. 滥用通配符选择器,给所有元素设置通用样式 :很多新手图省事,用* { color: red; font-size: 14px; }给所有元素设置样式,结果导致表单元素、表格元素等出现意外的样式问题,优先级混乱,一定要避免,通配符选择器只用来清除默认的内外边距就行。
  2. 搞不懂通配符选择器的优先级,以为它的优先级很高 :很多新手以为通配符选择器选中了所有元素,优先级就很高,结果写了* { color: red; },又写了p { color: blue; },发现 p 标签是蓝色的,就懵了。一定要记住,通配符选择器的优先级是最低的,比标签选择器还低,任何其他选择器都会覆盖它的同属性样式。
  3. 用通配符选择器设置的样式,被继承的样式覆盖了 :很多新手会反过来,写了body { color: red; },又写了* { color: blue; },以为继承的样式会覆盖通配符的样式,结果所有元素都是蓝色的。一定要记住,通配符选择器的优先级,比继承的样式要高,只要通配符设置了同属性的样式,继承的样式就会被覆盖。
  4. 用通配符选择器设置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;
}

这里给大家补充几个核心的语法细节,新手一定要记牢:

  1. 多个选择器之间,必须用空格分隔,绝对不能用其他符号:空格是后代选择器的标志,浏览器看到选择器之间的空格,就知道这是后代选择器。如果用了逗号、大于号等其他符号,就变成了其他的选择器,完全不一样的效果,一定要注意。
  2. 父元素选择器和后代元素选择器,可以是任意的基础选择器:标签选择器、类选择器、id 选择器都可以,没有任何限制。比如:
    • 标签选择器 + 标签选择器:div p;
    • 类选择器 + 标签选择器:.nav li;
    • id 选择器 + 标签选择器:#header a;
    • 类选择器 + 类选择器:.box .item;
  3. 后代选择器可以有多个层级,不止两个 :比如div ul li a,这是 4 个层级的后代选择器,选中<div>里的<ul>里的<li>里的<a>标签,完全合法,没有层数限制。
  4. 后代选择器只会选中空格后面的后代元素,不会选中空格前面的父元素 :比如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% 以上的复合选择器,都是后代选择器,我们给大家总结了它的核心适用场景:

  1. 选中某个父元素里的所有同名后代元素 :比如选中导航栏里的所有<a>标签,选中商品列表里的所有<li>标签,选中文章内容里的所有<p>标签。
  2. 差异化选择不同父元素里的同名元素 :比如<ul>里的<li>是一种样式,<ol>里的<li>是另一种样式;头部里的<a>是白色的,内容区里的<a>是蓝色的。
  3. 限定样式的生效范围,避免全局样式污染 :比如你想给某个模块里的按钮设置特殊样式,只需要写.module .btn,就只会选中这个模块里的按钮,不会影响页面里其他的按钮,完美避免样式污染。
  4. 组合类选择器、id 选择器,实现精准的元素定位:这是最常用的场景,通过类名限定父元素的范围,再选中里面的后代元素,精准度极高,不会影响其他元素。
3.1.7踩坑指南
  1. 后代选择器的多个选择器之间,没有用空格分隔,用了其他符号 :比如把div p写成了divp、div,p、div>p,这些都不是后代选择器,完全不一样的效果,一定要记住,后代选择器的标志是空格,必须用空格分隔多个选择器。
  2. 搞反了父元素和后代元素的顺序 :比如你想选中<div>里的<p>标签,结果写成了p div,这会选中<p>里的<div>标签,完全和你的需求相反,一定要记住,父元素选择器写在前面,后代元素选择器写在后面,和 HTML 的嵌套顺序一致。
  3. 后代选择器写得太长,嵌套层级太深 :比如写了body div.main div.content ul li p a,嵌套了 7 层,可读性极差,后期维护起来非常麻烦,而且浏览器解析的性能也会受影响。推荐大家后代选择器的层级尽量不要超过 3 层,超过 3 层的话,最好通过加类名来简化。
  4. 父元素选择器写得太宽泛,导致全局样式污染 :比如写了div a,结果页面里所有<div>里的<a>都被改了样式,包括你不想改的元素。一定要给父元素加一个唯一的类名,用类选择器来限定范围,比如.content a,避免样式污染。
  5. 以为后代选择器只会选中亲儿子,不会选中孙子元素 :很多新手刚学的时候,以为div p只会选中<div>的直接子元素<p>,结果孙子元素里的<p>也被选中了,就懵了。一定要记住,后代选择器会选中所有的后代,不管嵌套多少层,只要是在父元素里面的,都会被选中。如果只想选中亲儿子,要用子选择器,不是后代选择器。
  6. 后代选择器里的类名拼写错误 :比如 CSS 里写的是.nav li,HTML 里的类名是class="nev",拼写错了,自然匹配不上,样式不生效,一定要仔细检查类名的拼写,确保 CSS 和 HTML 里的完全一致。
  7. 以为后代选择器会影响父元素 :很多新手写了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;
}

这里给大家补充几个核心的语法细节,新手一定要记牢,和后代选择器区分开:

  1. 多个选择器之间,必须用大于号>分隔,绝对不能用空格 :大于号是子选择器的标志,浏览器看到>,就知道这是子选择器,只会选中直接子元素。如果用了空格,就变成了后代选择器,效果完全不一样,一定要注意区分。
  2. 大于号>的前后,可以加空格,也可以不加,但是推荐加空格,可读性更强 :比如div>a和div > a,效果是完全一样的,但是div > a的写法,空格把大于号和选择器分开,可读性更强,更符合行业规范,推荐大家这么写。
  3. 父元素选择器和直接子元素选择器,也可以是任意的基础选择器 :标签选择器、类选择器、id 选择器都可以,没有任何限制,比如.nav > li、#header > .nav、.box > .item。
  4. 子选择器也可以有多个层级,不止两个 :比如div > ul > li,这是两层子选择器,选中<div>的直接子元素<ul>的直接子元素<li>,完全合法,但是层级多了之后,可读性会变差,不推荐写太多层级。
  5. 和后代选择器一样,子选择器只会选中大于号后面的直接子元素,不会选中大于号前面的父元素 :比如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 适用场景:需要精准控制直接子元素,避免样式污染的场景

子选择器的适用场景非常明确,主要是以下几种:

  1. 导航栏的一级菜单和二级菜单:就像我们上面的示例,用子选择器控制一级菜单的样式,不会影响二级菜单,这是最经典的适用场景。
  2. 只需要控制父元素的直接子元素,不想影响里面嵌套的其他元素 :比如一个卡片容器,我们只想给它的直接子元素设置上下外边距,不想影响卡片里面嵌套的其他元素的间距,就可以用子选择器.card > *。
  3. 避免样式污染,限制样式的作用层级:在复杂的页面里,用子选择器可以精准控制样式的作用层级,不会不小心影响到更深层级的元素,避免全局样式污染。
  4. 组件化开发里的样式隔离:在 Vue、React 等组件化开发里,子选择器经常用来控制组件根元素的直接子元素的样式,不会影响到组件内部嵌套的子组件的样式,实现样式隔离。
3.2.8 踩坑指南
  1. 把子选择器的大于号>写成了空格,变成了后代选择器 :这是新手最容易犯的错误,把.two > a写成了.two a,结果孙子元素也被选中了,和预期的效果不一样,一定要记住,子选择器的标志是大于号>,不是空格。
  2. 大于号>的前后没有加空格,可读性极差 :比如写成div>a,虽然效果一样,但是可读性很差,推荐大家写成div > a,大于号前后加空格,代码更清晰,更容易维护。
  3. 搞反了父元素和子元素的顺序 :比如想选中<div>的亲儿子<a>,结果写成了a > div,这会选中<a>的亲儿子<div>,完全和需求相反,一定要记住,父元素写在前面,子元素写在后面。
  4. 以为子选择器会选中孙子元素:很多新手刚学的时候,以为子选择器和后代选择器一样,会选中所有的后代,结果孙子元素没有被选中,就以为样式不生效。一定要记住,子选择器只会选中直接子元素,孙子元素不会被选中,想要选中孙子元素,要用后代选择器。
  5. 子选择器的层级写得太多,可读性极差 :比如写成div > ul > li > p > span > a,嵌套了 6 层,可读性极差,后期维护起来非常麻烦,推荐子选择器的层级尽量不要超过 3 层。
  6. 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;
}

这里给大家补充几个核心的语法细节,新手一定要记牢:

  1. 多个选择器之间,必须用逗号,分隔,绝对不能用空格、大于号等其他符号:逗号是并集选择器的标志,浏览器看到逗号,就知道这是并集选择器,会把逗号前后的选择器,都应用后面的样式。如果用了其他符号,就变成了其他的选择器,效果完全不一样。

  2. 推荐每个选择器单独占一行,竖着写,不要横着写 :比如上面的示例,每个选择器占一行,逗号放在上一行的结尾,这样的写法,可读性极强,一眼就能看清有哪些选择器,后期要加、减选择器的时候,也非常方便。如果横着写div, p, h1, .btn { ... },选择器多了之后,可读性会变得极差,非常不推荐。

  3. 最后一个选择器的后面,绝对不能加逗号 :这是新手最容易犯的错误,最后一个选择器的后面加了逗号,比如:

    复制代码
    div,
    p,
    h1, { /* 这里最后一个选择器h1后面加了逗号,是错误的 */
        color: red;
    }

    虽然部分现代浏览器会自动忽略这个多余的逗号,但是很多老版本的浏览器(比如 IE),会解析失败,导致整个样式块完全不生效,一定要注意,最后一个选择器的后面,绝对不能加逗号。

  4. 逗号分隔的每个选择器,可以是任意类型的选择器 :标签选择器、类选择器、id 选择器、后代选择器、子选择器,都可以放在并集选择器里,没有任何限制,比如:

    复制代码
    /* 并集选择器里,包含了标签选择器、类选择器、后代选择器、子选择器 */
    p,
    .btn,
    .nav li,
    .box > a {
        color: red;
        font-size: 16px;
    }
  5. 并集选择器里的每个选择器,都是完全独立的,互相之间没有任何影响 :浏览器会把并集选择器拆开来,分别解析每个选择器,然后给每个选择器对应的元素,应用后面的样式。比如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 适用场景:只要有重复的样式,就可以用

并集选择器的适用场景极其广泛,只要你有多个不同的选择器,需要设置相同的样式,就可以用并集选择器,我们给大家总结了最核心的几个适用场景:

  1. 提取多个选择器的公共样式,消除代码冗余:这是最核心、最常用的场景,只要有重复的样式,就用并集选择器合并起来。
  2. CSS 全局重置,给多个标签设置统一的重置样式:比如给所有的标题标签、列表标签、表单元素,设置统一的重置样式,这是并集选择器的经典用法。
  3. 给页面里多个不同的元素,设置统一的基础样式:比如给页面里所有的按钮、输入框、卡片,设置统一的字体、行高、圆角,用并集选择器合并起来,只写一遍。
  4. 给多个不同的状态,设置相同的样式 :比如给链接的:link、:visited、:active状态,设置相同的文字颜色,用并集选择器合并起来。

简单来说:只要你发现自己在给多个不同的选择器,写一模一样的样式,就一定要用并集选择器把它们合并起来,这是前端开发的基本素养。

3.3.7 踩坑指南
  1. 多个选择器之间,用了空格而不是逗号,变成了后代选择器 :这是新手最容易犯的错误,把div, p写成了div p,结果变成了后代选择器,选中了 div 里的 p 标签,完全和预期的效果不一样,一定要记住,并集选择器的标志是逗号,,不是空格。
  2. 最后一个选择器的后面,加了多余的逗号 :很多新手在竖着写的时候,会给最后一个选择器的后面也加一个逗号,比如div, p, { ... },这会导致老版本的浏览器解析失败,样式完全不生效,一定要注意,最后一个选择器的后面,绝对不能加逗号。
  3. 把所有的选择器都横着写在一行里,可读性极差 :比如div, p, h1, .btn, .nav li, .card > a { ... },选择器多了之后,一行根本看不完,可读性极差,后期要加、减选择器的时候,也非常麻烦。一定要推荐每个选择器单独占一行,竖着写,代码更清晰,可读性更强。
  4. 以为并集选择器里的选择器是有关联的,结果理解错了效果 :很多新手以为div, p { color: red; }是选中 div 里的 p 标签,其实不是,它是选中所有的 div 和所有的 p 标签,两个选择器是完全独立的,没有任何关联,一定要搞清楚,并集选择器的逗号是 "和" 的意思,不是 "里的" 的意思。
  5. 并集选择器里的选择器拼写错误 :比如 CSS 里写的是.btn, .title { ... },HTML 里的类名是.titel,拼写错了,自然匹配不上,样式不生效,一定要仔细检查每个选择器的拼写。
  6. 滥用并集选择器,把完全不相关的选择器合并在一起:很多新手为了省事,把页面里所有的选择器都合并到一个并集选择器里,结果导致代码逻辑混乱,后期要修改的时候,根本找不到对应的选择器,维护成本反而更高。一定要记住,只有设置完全相同样式的选择器,才需要合并到并集选择器里,完全不相关的选择器,不要随便合并。

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 { 样式声明 }

这里给大家补充几个核心的语法细节:

  1. 元素选择器和伪类名之间,必须用冒号:连接,中间绝对不能加空格:比如a:hover是正确的,a :hover是错误的,加了空格之后,就变成了后代选择器,选中 a 标签里的:hover 元素,完全不是我们想要的效果,一定要注意。
  2. 必须按照 LVHA 的顺序书写,绝对不能乱序:这是链接伪类选择器最重要的规则,没有之一。四个伪类选择器,必须按照a:link → a:visited → a:hover → a:active的顺序书写,简称LVHA 顺序,如果顺序乱了,部分伪类的样式会失效,我们后面会详细讲为什么必须按这个顺序。
  3. 伪类选择器的冒号,是英文的冒号,绝对不能用中文的冒号:和其他 CSS 语法一样,必须用英文符号,中文冒号会导致样式不生效。
  4. 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>

大家把这段代码用浏览器打开,就能完整体验链接的四个状态的效果:

  1. 未访问状态 :刚打开页面的时候,链接是黑色的,没有下划线,a:link生效;
  2. 鼠标悬停状态 :把鼠标移到链接上,不要点击,链接会变成红色、加粗、带下划线,a:hover生效;
  3. 鼠标按下状态 :按住鼠标左键,不要松开,链接会变成蓝色,a:active生效;
  4. 已访问状态 :松开鼠标,跳转到百度,然后回到这个页面,链接会变成绿色,a:visited生效。

如果大家想让已经访问过的链接,恢复成未访问的状态,只需要清空浏览器的历史记录 就行,Windows 系统可以按Ctrl + Shift + Delete,Mac 系统可以按Command + Shift + Delete,快速打开清除历史记录的面板。

3.4.5 为什么必须严格按照 LVHA 顺序书写?底层原理彻底讲透

很多新手会问:"为什么必须按 LVHA 的顺序写?我乱序写会怎么样?",这里我们用 CSS 的层叠性,给大家彻底讲透底层原理,让大家不仅知其然,更知其所以然,不用死记硬背,也能记住这个顺序。

首先,我们要明确两个核心前提:

  1. 这四个伪类选择器的优先级,是完全相同的:它们都是 "标签选择器 + 伪类选择器",优先级完全一样。
  2. 当多个优先级相同的样式,同时作用于同一个元素时,写在后面的样式,会覆盖写在前面的同属性样式:这是 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 适用场景:所有需要交互反馈的元素

伪类选择器的适用场景极其广泛,只要是需要给用户交互反馈的元素,都可以用:

  1. 超链接的状态样式:给链接的不同状态设置不同的样式,给用户清晰的反馈。
  2. 按钮的悬停、点击效果:给按钮设置鼠标悬停、按下的样式,提升按钮的交互体验,这是最常用的场景。
  3. 卡片、列表项的悬停效果:给卡片、列表项设置悬停时的阴影、上浮、变色效果,提升页面的质感和体验。
  4. 导航菜单的悬停、选中效果:给导航菜单项设置悬停、当前选中的样式,让用户清晰地知道自己在哪个页面。
  5. 表单元素的状态样式:给输入框的获得焦点、禁用、必填状态设置不同的样式,给用户清晰的反馈,这是进阶的伪类选择器的用法,入门阶段了解即可。
3.4.10 新手必踩坑指南:这些错误 90% 的新手都犯过
  1. 没有按照 LVHA 顺序书写,导致部分伪类样式失效 :这是新手最容易犯的错误,把顺序写乱了,比如把a:hover写在a:link和a:visited的前面,导致悬停样式不生效,一定要严格按照 LVHA 顺序书写,记住 "绿化" 的口诀。
  2. 元素选择器和伪类名之间,加了空格 :比如把a:hover写成了a :hover,加了空格之后,变成了后代选择器,选中 a 标签里的:hover 元素,完全不生效,一定要记住,冒号前后不能加空格。
  3. 用了中文的冒号,导致样式不生效:把英文的冒号写成了中文的冒号,浏览器解析失败,样式完全不生效,写的时候一定要注意输入法的状态。
  4. 以为:hover只能用于 a 标签,不能用于其他元素 :很多新手刚学的时候,以为:hover只能给 a 标签用,其实它可以给任何 HTML 元素用,包括 div、p、button、img 等等,灵活度极高。
  5. a:link只对有 href 属性的 a 标签生效 :如果你的 a 标签没有写 href 属性,比如<a>测试</a>,a:link的样式是不会生效的,只有a的通用样式会生效,一定要给 a 标签加上 href 属性,哪怕是href="#"。
  6. a:visited里的样式限制 :很多新手会给a:visited设置背景图片、背景色、字体大小等样式,结果发现不生效。这是因为浏览器为了保护用户的隐私,限制了a:visited能设置的样式,只能修改颜色相关的属性(color、background-color、border-color 等),其他的属性都不能修改,这是浏览器的安全限制,不是你的代码写错了。
  7. 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 很强大,但是新手绝对不要用它,哪怕你的样式死活不生效,也不要用,原因有三个:

  1. 它会彻底破坏 CSS 的层叠性,让代码变得完全无法维护 CSS 的核心设计理念就是层叠性,通过优先级规则来自然地解决样式冲突。如果你随便用 !important,就相当于用皇帝的圣旨去强制覆盖所有的规则,后面你想要修改这个样式的时候,只能用另一个 !important 去覆盖它,最后你的 CSS 代码里会到处都是 !important,变成一团乱麻,哪怕是你自己,过一个月也看不懂,更别说其他开发者了。
  2. 它会让你无法真正理解 CSS 的优先级规则 很多新手遇到样式不生效的问题,就直接加 !important,虽然样式生效了,但是你永远不知道为什么之前的样式不生效,永远学不会 CSS 的优先级规则,永远无法真正掌握 CSS。
  3. 它会导致很多意外的样式问题 如果你给一个通用的样式加了 !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 选择器、类选择器、标签选择器都高,它会覆盖这些选择器里的同属性样式。

为什么尽量少用行内样式?

虽然行内样式的优先级很高,但是我们尽量不要用它,原因有三个:

  1. 它违反了 CSS 的核心设计理念:结构与样式分离 HTML 的作用是定义页面的结构(比如哪里是标题,哪里是段落,哪里是图片),CSS 的作用是定义页面的样式(比如标题是什么颜色,段落是什么大小)。如果你把样式写在 HTML 标签的 style 属性里,就把结构和样式混在一起了,后期维护的时候,你要修改样式,就得去改 HTML 代码,非常麻烦,也不符合前端开发的规范。
  2. 它的代码复用性为 0行内样式只能作用于当前这一个标签,如果你有 10 个标签都要用同样的样式,你就得给 10 个标签都写一遍一模一样的行内样式,代码冗余度极高,完全没有复用性。
  3. 它的优先级太高,后期很难覆盖 行内样式的优先级仅次于 !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 属性写错了)。

解决方法:

  1. 打开浏览器的开发者工具(按 F12),切换到 Elements 面板,选中那个元素,看右边的 Styles 面板:
    • 看看你的类选择器的样式是不是被划掉了,如果被划掉了,说明被更高优先级的样式覆盖了;
    • 看看上面是不是有 id 选择器、行内样式的同属性样式;
    • 看看是不是有另一个类选择器写在后面,覆盖了你的样式。
  2. 检查你的类选择器的拼写,确保 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 个坑

  1. 绝对不要滥用 !important :哪怕你的样式死活不生效,也不要用 !important,先去检查是不是选择器写错了,是不是优先级不够,用正确的方法解决问题。
  2. 尽量不要用行内样式:所有的样式都尽量写在 CSS 里,用选择器去选中元素,实现结构与样式分离。
  3. 尽量不要用 id 选择器设置 CSS 样式:id 选择器主要留给 JavaScript 用,CSS 样式优先用类选择器,类选择器更灵活,复用性更高。
  4. 不要以为选择器写得越长,优先级越高 :很多新手以为 div ul li p .red 比 .red 的优先级高,其实不是,优先级只看选择器的类型,不看选择器的长度。div ul li p .red 还是类选择器,优先级和 .red 一样,只是更具体一点。
  5. 不要忘记层叠性的规则:优先级相同的时候,后面的覆盖前面的,写代码的时候要注意选择器的顺序。

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 选择器,在前端的世界里,创造出更多精彩的页面。

相关推荐
尼给路达哟1 小时前
10.14面试高级python和agent
面试·职场和发展
m0_587383001 小时前
碰碰好评卡功能落地实战 从需求梳理到上线部署全流程实操指南
java·linux·服务器·前端·javascript·vue.js·人工智能
Pu_Nine_91 小时前
Vue‑Flow 从零入门实战|AI‑Agent 拓扑图开发
前端·javascript·vue.js
keyipatience1 小时前
CSS 学习(1):语法、引入方式、选择器、字体与背景
前端·css·html
普通人61 小时前
php处理百度编辑器有误html
开发语言·编辑器·html·php·1024程序员节
程序人生8881 小时前
本地向量底座怎么搭?DocConverter Web 的 BGE 嵌入模型、ChromaDB 与外部 Milvus 接入
前端·milvus
恋猫de小郭1 小时前
Flutter Android 改动:APK 不再允许通过 Intent 动态修改 Engine 参数
android·前端·flutter
程序员大阳1 小时前
副队长HTML+CSS博客网站实战 (5)-文章详情页 article.html开发
css·html·博客网站·详情页
ck0135791 小时前
视频转 GIF 的基本流程与文件大小优化
前端·测试工具·html·音视频·视频