Skip to content

复合选择器、状态与层叠 ​

真实页面不能为每个标签单独创建类。本节学习根据结构、属性和状态选择元素,并建立一套判断“为什么这条样式没有生效”的方法。

组合条件定位元素 ​

后代选择器 ​

下方是这张图的内联 HTML 示意。h1 与 em 之间的空格表示后代关系:

外层标签:h1空格 → 后代关系后代标签:em
h1 em {
color: red;
}
匹配示例:<h1>标题 <em>重点</em></h1> 中的 em
  • 概念:

    后代选择器又称为包含选择器

  • 作用:

    用来选择元素或元素组的 子孙后代

    其写法就是把外层标签写在前面,内层标签写在后面,中间用 空格 分隔,先写父亲爷爷,在写儿子孙子。

    父级 子级{属性:属性值;属性:属性值;}
  • 语法:

    css
    .student-card p {color:red; font-size:16px;}

    空格表示选择 .student-card 内部任意层级的 p。

    html
    
    <article class="student-card">
        <div>
            <p>这一段也会被选中。</p>
        </div>
    </article>

不要把选择器写得过长。.page .content .list .item .name 对结构依赖过强,页面稍作调整就可能失效。

子代选择器 ​

下方是图片中的 h1 > em 示意。> 只选中直接子元素:

父级标签:h1> 直接子代子级标签:em
h1 > em {
color: red;
}
会匹配:<h1><em>重点</em></h1>
不会匹配:<h1><span><em>重点</em></span></h1>
  • 作用:

    子元素选择器只能选择作为某元素 子元素 (亲儿子) 的元素。

    其写法就是把父级标签写在前面,子级标签写在后面,中间跟一个 > 进行连接

  • 语法:

    css
    .main-nav > a {color: #ffffff;}

    > 只匹配直接子元素,这里的子指的是 亲儿子 不包含 孙子、重孙子 之类。:

    html
    
    <nav class="main-nav">
        <a href="/students">学员</a>
        <div>
            <a href="/students/import">导入</a>
        </div>
    </nav>

    上面的规则只选择第一个直接位于 nav 下的链接。

交集选择器 ​

我们可以给标签指定多个类名,从而达到更多的选择目的。

注意:

  • 各个类名中间用空格隔开。
  • 多类名选择器在后期布局比较复杂的情况下,还是较多使用的。

图片中的两种写法都是交集选择器,条件必须由同一个元素同时满足:

多个条件写在一起,中间不留空格
h1.very { color: red; }
标签 h1 + 类名 very
匹配:<h1 class="very">
h1#gaudi { color: red; }
标签 h1 + ID gaudi
匹配:<h1 id="gaudi">

选择同一个元素的多个条件之间不加空格:

css
.status.is-active {
    color: #16803c;
}

button.primary-button {
    background-color: #1677ff;
}

.status.is-active 表示同时拥有两个类;.status .is-active 表示后代关系。

并集选择器 ​

图片用逗号把标签、ID 和类选择器合并,三类元素共享同一条声明:

a:标签#guide:ID.very:类
a,#guide,.very{
color: red;
}
逗号表示“或”:满足任意一项就会应用这条规则。

多个选择器需要使用同一组声明时,用逗号组成并集选择器:

这张图还展示了并集选择器可以共用多条声明:

.class:类选择器h3:标签选择器
.class, h3 {
color: red;
font-size: 25px;
}
声明 1:color → red声明 2:font-size → 25px

标签、类、ID 和其他组合选择器都可以参与分组。例如 .one, p, #test { color: #f00; } 会同时选中 .one、所有 p 和 #test,避免重复书写相同声明。

属性选择器 ​

图片中的 a[name] 由标签名称和方括号中的属性条件组成:

a:标签选择器[name]:属性存在条件
a[name] {
color: red;
}
匹配条件:元素是 a,并且带有 name 属性。
css
input[required] {
    border-color: #d97706;
}

input[type="search"] {
    background-color: #ffffff;
}

[data-status="disabled"] {
    color: #b42318;
}

常见形式:

写法含义
[required]拥有该属性
[type="search"]属性值完全相等
[class~="active"]空格分隔的属性值列表中包含该单词

基础项目优先使用类表达视觉状态;属性选择器适合表单类型、原生状态和确有含义的数据属性。

现代逻辑伪类 ​

:is() 可以把多个选择器条件合并到一条规则中,同时保留参数中最高的特异度:

css
:is(.page-title, .dialog-title) {
    color: #17233d;
}

:where() 的写法相似,但特异度始终为 0,适合提供低优先级的基础规则:

css
:where(.page, .dialog) a {
    color: #1677ff;
}

这两个伪类都用于减少重复选择器,但不能把 :is() 和 :where() 当成完全相同的写法。

根据状态选择元素 ​

链接伪类选择器 ​

图片中的 :link 用于选择尚未访问的链接状态:

a:标签选择器:link:未访问状态
a:link {
color: red;
}
匹配对象:具有链接地址且尚未访问的 <a> 元素。

为了和我们刚才学的类选择器相区别

类选择器是一个点 比如 .demo {}

在 CSS 中,我们可以使用“超链接伪类”来定义超链接在鼠标点击的不同时期的样式。

  • 语法:

    css
    a:link { … }
    a:visited { … }
    a:hover { … }
    a:active { … }
  • 说明:

伪类说明
a:link定义 a 元素未访问时的样式
a:visited定义 a 元素访问后的样式
a:hover定义鼠标经过 a 元素时的样式
a:active定义鼠标点击激活时的样式

如果同时定义多个链接状态,通常按照 :link、:visited、:hover、:active 的 LVHA 顺序书写,避免后面的规则覆盖前面的规则。实际项目不一定需要定义全部四种状态。

  • 多个伪类同时作用于同一个元素时,中间不加空格,例如 a:link:hover;空格表示后代关系。
  • 因为 a 链接浏览器具有默认样式,所以我们实际工作中都需要给链接单独指定样式。
  • 实际工作开发中,我们很少写全四个状态,一般我们写法如下:
css
a {
	/* a 是标签选择器  所有的链接 */
	font-weight: 700;
	font-size: 16px;
	color: gray;
}

a:hover {
	/* :hover 是链接伪类选择器 鼠标经过 */
	color: red;
	/*  鼠标经过的时候,由原来的 灰色 变成了红色 */
}

用户操作伪类 ​

伪类表示元素处于某种状态,以 : 开头。

css
.nav-link:hover {
    background-color: #eef4ff;
}

.form-control:focus {
    border-color: #1677ff;
    outline: 3px solid rgb(22 119 255 / 18%);
}

.primary-button:disabled {
    cursor: not-allowed;
    opacity: 0.55;
}
  • :hover:指针悬停。
  • :focus:元素获得焦点。
  • :disabled:表单控件被禁用。
  • :checked:单选或复选控件被选中。

不要只设计 :hover。键盘用户需要清楚的焦点提示。现代项目也常用 :focus-visible:

css
.nav-link:focus-visible {
    outline: 3px solid #fbbf24;
    outline-offset: 2px;
}

结构伪类 ​

css
.student-table tbody tr:first-child {
    font-weight: 600;
}

.student-table tbody tr:last-child {
    border-bottom: 0;
}

.student-table tbody tr:nth-child(even) {
    background-color: #f8fafc;
}
  • :first-child:同级中的第一个子元素。
  • :last-child:同级中的最后一个子元素。
  • :nth-child(even):同级中的偶数位置。

位置是根据同一父元素下的全部兄弟元素计算的。复杂公式不属于本课程重点。

1、属性列表

选择符简介
E:first- child匹配父元素中的第一个子元素 E
E:last-child匹配父元素中最后一个 E 元索
E:nth-child(m)匹配父元素中的第 n 个子元素 E
E:first-of-type指定类型 E 的第一个
E:last-of-type指定类型 E 的最后一个
E:nth-of-type(n)指定类型 E 的第 n 个

2、代码演示

css
ul li:first-child {background-color: lightseagreen;}
ul li:last-child {background-color: lightcoral;}
ul li:nth-child(3) {background-color: aqua;}

nth-child 参数详解

1、nth-child 详解

  • 注意:本质上就是选中第几个子元素
  • n 可以是数字、关键字、公式
  • n 如果是数字,就是选中第几个
  • 常见的关键字有 even 偶数、odd 奇数
  • 常见的公式如下 (如果 n 是公式,则从 0 开始计算)
  • 但是第 0 个元素或者超出了元素的个数会被忽略
公式取值
2n偶数
2n + 1奇数
5n5 10 15 ...
n + 5从第 5 个开始(包含第五个)到最后
-n + 5前 5 个(包含第五个)

2、代码演示

css
/* 偶数 */
ul li:nth-child(even) { background-color: aquamarine; }

/* 奇数 */
ul li:nth-child(odd) { background-color: blueviolet; }

/*n 是公式,从 0 开始计算 */
ul li:nth-child(n) { background-color: lightcoral; }

/* 偶数 */
ul li:nth-child(2n) { background-color: lightskyblue; }

/* 奇数 */
ul li:nth-child(2n + 1) { background-color: lightsalmon; }

/* 选择第 0 5 10 15, 应该怎么选 */
ul li:nth-child(5n) { background-color: orangered; }

/* n + 5 就是从第 5 个开始往后选择 */
ul li:nth-child(n + 5) { background-color: peru; }

/* -n + 5 前五个 */
ul li:nth-child(-n + 5) { background-color: tan; }

伪元素 ​

选择符简介
::before在元素内部的签名插入内容
::after在元素内部的后面插入内容

伪元素表示元素中的一个虚拟部分,使用 :::

css
.required-label::after {
    content: " *";
    color: #b42318;
}

.page-title::before {
    content: "";
    display: inline-block;
    width: 4px;
    height: 20px;
    margin-right: 8px;
    background-color: #1677ff;
}

::before 和 ::after 依赖 content。它们适合装饰,不应承载只有屏幕上才有的关键业务信息。

判断最终生效的样式 ​

层叠 ​

图片中的两条规则都设置了 div 的颜色。后一条覆盖前一条的 color,但不会清除前一条的 font-size:

先写的规则
div {
  color: red;
  font-size: 18px;
}
后写的规则
div {
  color: green;
}
最终生效:color: green; 和 font-size: 18px;
  • 概念:

    所谓层叠性是指多种 CSS 样式的叠加。

    是浏览器处理冲突的一个能力,如果一个属性通过两个相同选择器设置到同一个元素上,那么这个时候一个属性就会将另一个属性层叠掉

  • 原则:

    样式冲突时,不能只看哪条规则“离 HTML 更近”。浏览器会先判断声明的来源与重要性,再比较层叠层级、选择器特异度,最后才比较相同条件下的源码顺序。

    样式不冲突时,不同声明可以同时生效;只有同一属性发生竞争时才需要判断层叠结果。

继承 ​

图片中的 p 没有单独设置文字颜色和字号,会继承父元素 div 的这两项值:

父元素样式
div {
  color: red;
  font-size: 18px;
}
HTML 结构
<div>
  <p>文本颜色</p>
</div>
子元素 p 继承:color: red;、font-size: 18px;
  • 概念:

    子标签会继承父标签的某些样式,如文本颜色和字号。

    想要设置一个可继承的属性,只需将它应用于父元素即可。

    简单的理解就是:子承父业。

    文字相关属性通常可以继承;背景、边框、外边距和宽高通常不会继承。开发者工具会标出继承来源。不要用“所有 CSS 都会继承”来理解页面。

  • 注意:

  • 恰当地使用继承可以简化代码,降低 CSS 样式的复杂性。比如有很多子级孩子都需要某个样式,可以给父级指定一个,这些孩子继承过来就好了。- 子元素可以继承父元素的样式(text-,font-,line-这些元素开头的可以继承,以及 color 属性)

CSS 继承性口诀:龙生龙,凤生凤,老鼠生的孩子会打洞。

优先级 ​

图片中两个选择器都匹配 <div class="box">。类选择器 .box 的特异度高于标签选择器 div,因此颜色取 green;字号只在 div 规则中声明,仍然生效:

类选择器:特异度较高
.box {
  color: green;
}
标签选择器:特异度较低
div {
  color: red;
  font-size: 18px;
}
最终生效:color: green; 和 font-size: 18px;
  • 概念:

定义 CSS 样式时,经常会有两个或更多规则同时匹配同一个元素。选择器相同或特异度相近时,需要继续结合来源、重要性和源码顺序判断最终结果。

在不讨论来源、重要性和层叠层级的简化练习中,可以这样比较特异度:

text
行内样式 > ID 选择器 > 类/属性/伪类 > 元素/伪元素
css
:where(.page, .dialog) a {
    color: #1677ff;
}

项目中遇到时知道它不会增加选择器优先级即可,不要求现在使用。

练习优先级判断 ​

题目 1

html
<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>第1题</title>
    <style>
        div div {color: blue;}
        div {color: red;}
    </style>
</head>
<body>
<div>
    <div>
        <div>
            试问这行字体是什么颜色的?
        </div>
    </div>
</div>
</body>
</html>

题目 2

html
<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>第2题</title>
    <style>
        #father {color: red;}
        p {color: blue;}
    </style>
</head>
<body>
<div id="father">
    <p>试问这行字体是什么颜色的?</p>
</div>
</body>
</html>

题目 3

html
<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        div div div div div div div div div div div div {color: red;}
        .me {color: blue;}
    </style>
</head>
<body>
<div>
    <div>
        <div>
            <div>
                <div>
                    <div>
                        <div>
                            <div>
                                <div>
                                    <div>
                                        <div>
                                            <div class="me">试问这行文字是什么颜色的</div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

题目 4

html
<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>第4题</title>
    <style>
        div p {color: red;}
        #father {color: yellow;}
        p.c2 {color: blue;}
    </style>
</head>
<body>
<div id="father" class="c1">
    <p class="c2">
        试问这行字体是什么颜色的?
    </p>
</div>
</body>
</html>

题目 5

html
<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        .c1 .c2 div {color: blue;}
        div #box3 {color: green;}
        #box1 div {color: yellow;}
    </style>
</head>
<body>
<div id="box1" class="c1">
    <div id="box2" class="c2">
        <div id="box3" class="c3">
            文字
        </div>
    </div>
</div>
</body>
</html>

题目 6

html
<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>第6题</title>
    <style>
        #father #son {color: blue;}
        #father p.c2 {color: black;}
        div.c1 p.c2 {color: red;}
        #father {color: green !important;}
/*!important 会改变声明优先级,不是让特异度变成无限大*/
        p {color: green !important;}
    </style>
</head>
<body>
<div id="father" class="c1">
    <p id="son" class="c2" >
        试问这行字体是什么颜色的?
    </p>
</div>
</body>
</html>

小结 ​

  1. 组合选择器可以根据结构、多个条件和属性定位元素。
  2. 伪类表达状态,伪元素适合非关键装饰。
  3. 字体和颜色常会继承,盒模型相关属性通常不会。
  4. 样式冲突要按匹配、来源、优先级和顺序判断。
  5. 选择器应尽量短、稳定并围绕组件职责组织。
选择器与层叠练习源码
html
<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>第6题</title>
    <style>
        #father #son {color: blue;}
        #father p.c2 {color: black;}
        div.c1 p.c2 {color: red;}
        #father {color: green !important;}
        /*!important 会改变声明优先级,不是让特异度变成无限大*/
        p {color: green !important;}
    </style>
</head>
<body>
<div id="father" class="c1">
    <p id="son" class="c2">
        试问这行字体是什么颜色的?
    </p>
</div>
</body>
</html>