07选择器进阶

基础篇学会了「点名」:标签、class、id。这一课把点名的功夫练到出神入化—— 按「亲戚关系」找(组合选择器)、按「随身证件」找(属性选择器)、按「排行」找(结构伪类)、 按「此刻状态」找(状态伪类),甚至凭空造零件(伪元素)。 最后用「优先级」一锤定音:规则打架时,到底听谁的。

约 25 分钟 6 个动手实验 核心功力课

一、组合选择器:四种「亲戚关系」

单独一个选择器只能描述「是谁」,组合选择器描述「和谁是什么关系」。 四个符号——空格、>+~——就是四种亲戚关系, 再加一个逗号管「分组」。

四种组合器 + 逗号分组,一张表认全
写法名称选中的是谁记忆口诀
A B(空格)后代选择器A 里面任意层级的 B,隔多少层都算「A 家里所有叫 B 的」
A > B子代选择器A 的直接孩子 B,孙子免谈「A 的亲儿子」
A + B相邻兄弟紧跟在 A 后面的第一个同级 B「A 身后紧跟的那位」
A ~ B通用兄弟A 后面的所有同级 B「A 身后全体兄弟」
A, B(逗号)分组选择器A 和 B 一起选中,共用一套样式「你们俩一起上」
关系地图:上半场比「空格 vs >」,下半场比「+ vs ~」。试试把 .box > p> 删掉,看孙子级 p 也被染绿
能用 > 就别用空格:一是意图更明确(读者一眼知道只影响子级),二是性能更好(浏览器不用一层层往下翻)。改别人代码时,空格选择器改错一个层级,样式就会「莫名感染」。

二、属性选择器:不用 class 也能精准点名

元素身上的属性——hreftypesrc、自定义的 data-*—— 本身就是现成的「证件」。方括号 [ ] 就是查证件的窗口: 查得越细,选得越准,一个 class 都不用加。

属性选择器六类写法逐项分析
写法命中条件典型用途
[href]有这个属性就行所有带链接的 a
[type="text"]属性值完全相等只选文本框,跳过按钮和勾选框
[href^="https"]值以 https 开头挑出所有外部链接
[href$=".pdf"]值以 .pdf 结尾给文件下载链接加图标
[href*="runoob"]值里包含 runoob关键词模糊匹配
[data-tags~="css"]值包含独立词 css(空格分隔的完整单词)标签系统:一个属性塞多个标签
同一页链接按 href 特征自动分组染色:第一条同时命中两条规则,样式叠加。把 ^="https" 改成 $=".com" 看命中变化
符号记忆:^ 像钉子钉在开头$ 像尾巴收在结尾* 是通配符表示随便包含~= 最容易踩坑:它只认「空格分隔的完整单词」——标签写 css3 时,~="css" 选不中它,*="css" 却能。做标签筛选时想清楚要哪种。

三、结构伪类:第几个孩子我说了算

「第 3 个 li」「偶数行」「每种类型的第一个」……结构伪类专管排行。 冒号开头,跟在选择器屁股后面,专治「不方便挨个加 class」的批量场景。

高频结构伪类速查
伪类含义高频场景
:first-child是父亲的第一个孩子导航第一项去分隔线
:last-child是父亲的最后一个孩子列表末项去底边框
:nth-child(n)第 n 个(从 1 数起)点名任意一位
:nth-child(odd) / (even)奇数 / 偶数位表格斑马纹
:nth-child(3n+1)公式:第 1、4、7… 个每 3 个换一组
:nth-last-child(n)从后往前数第 n 个倒数第几条
:first-of-type同类型里的第一个混排容器的救星(见下方坑)
:nth-of-type(n)同类型里的第 n 个每隔 2 张图片加边距
:only-child独生子:父亲只有它一个孩子单条数据特殊排版

:nth-child 的 an+b 公式口算

an+bn 从 0 开始自动递增(0、1、2、3…),把 n 逐个代进去, 算出来是几就选第几个。背下这几个常用公式就够应付八成场景:

nth-child(an+b) 常用公式逐项分析
公式展开后是哪些(n = 0,1,2,3…)使用场景
odd / 2n+1第 1、3、5、7… 个斑马纹的奇数行
even / 2n第 2、4、6、8… 个斑马纹的偶数行
3n第 3、6、9… 个每行第 3 张卡片去掉右外边距
3n+1第 1、4、7、10… 个三列布局的每行开头
n+4第 4、5、6… 个(前 3 个不要)跳过前几条精选,选中其余
-n+3第 3、2、1 个(只选前 3 个)排行榜前三甲特殊样式
命中地图:条纹来自 odd/even,边框来自 first/last,虚线圈是 nth-child(3)。把 odd 改成 3n+1 看斑马纹变规律;下半场是 :first-child 的经典坑
:first-child 的意思是「恰好是父亲的第一个孩子」,不是「第一个这种元素」。写了 h5:first-child 却死活不生效,先检查:h5 前面是不是还站着别的标签?是的话换 h5:first-of-typenth-childnth-of-type 同理:前者混着数,后者分开数。

四、状态伪类:元素此刻的心情

同一个元素,不同时刻不同待遇:鼠标悬停时、正被按住时、获得焦点时、被勾选时、被禁用时…… 状态伪类让 CSS 能「看心情」办事,是交互反馈的地基。

高频状态伪类速查
伪类什么时候命中典型用法
:hover鼠标悬停在上面按钮/链接变色、上浮
:active正被按住的那一下按下缩小、加深,像真的压下去
:focus获得输入焦点(点击 / Tab 切入)输入框亮起描边
:focus-within自己或后代有焦点表单里任意框被点,整个表单高亮
:checked勾选框 / 单选被选中配合兄弟选择器做纯 CSS 开关
:disabled表单控件被禁用置灰 + 禁止光标
:link / :visited没点过 / 已经点过的链接已读链接变淡
交互全家桶:点输入框看 :focus:focus-within,悬停/按住按钮,勾选勾选框看 :checked + span 组合拳
这个演示里藏了三个「组合拳」:挑输入框用的是第 2 节的属性选择器 input[type="text"];勾选后的对勾是第 5 节的伪元素 ::after:checked + span 用了第 1 节的相邻兄弟。选择器从来都是组合出场的,这也是它们真正的威力所在。

五、伪元素:凭空造出「隐形零件」

伪类描述「状态」,伪元素更进一步——直接在元素内部 凭空生成一个假想的子元素。最常用的是 ::before::after 这对双胞胎,分别站在内容的正前方和正后方。双冒号是 HTML5 时代的规范写法 (老代码里的单冒号也能跑)。

常用伪元素速查
伪元素管哪块典型用法
::before内容前面的隐形盒子引号、图标、装饰色块、清除浮动
::after内容后面的隐形盒子闭合引号、「更多 »」箭头
::first-letter只管第一个字杂志式首字下沉
::first-line只管第一行导语第一行加粗变色
::selection鼠标划选中的文字自定义选中背景色
::placeholder输入框的占位提示提示文字变淡变小
::marker列表项的记号(圆点/数字)把圆点换成彩色方块
首字下沉 + 前后凭空生成的引号:试试用鼠标划选引用文字看 ::selection 换色;把 content 删掉看伪元素直接消失
content 是伪元素的命门——忘了写 content,整个伪元素直接不显示。不想插内容就写 content: ""(画装饰图形的标准姿势)。另外 ::before / ::after 默认是行内元素:想设宽高、做定位,得先加 display: block(或 inline-block / position: absolute)。

六、优先级:规则打架了,谁说了算

三条规则同时给一个元素上色,听谁的?CSS 有一套铁打的算法——权重(specificity)。 把选择器拆开数三档:(id 个数, class/属性/伪类个数, 标签/伪元素个数), 从左往右逐位比较,左边大的直接赢。

权重天梯:从行内样式到 !important
选择器权重地位
行内 style="…"1-0-0-0写在标签上,尽量少用
#id0-1-0-0一等公民,一票顶千军
.class / [attr] / :hover0-0-1-0主力部队,日常靠它们
标签 / ::before0-0-0-1基础兵,打底用
* / 组合符 + ~ >0-0-0-0不计数,纯关系
!important核弹同权重全灭,调试应急,别当饭吃

口算练习:这些选择器各是几比几

合上答案先自己数一遍——数 (id, class/属性/伪类, 标签/伪元素) 三档,再对照右边:

权重口算练习表(组合符 +、>、~、空格不计数;:not() 本身不算,括号里的算)
选择器拆解权重
p1 个标签0-0-1
.nav li1 个 class + 1 个标签0-1-1
.nav > .item:hover2 个 class + 1 个伪类0-3-0
ul li.active a1 个 class + 3 个标签0-1-3
#header .nav a1 个 id + 1 个 class + 1 个标签1-1-1
input[type="text"]:focus1 个属性 + 1 个伪类 + 1 个标签0-2-1
.list li:not(.empty)2 个 class + 1 个标签(:not 不计数,括号里的 .empty 计)0-2-1
上半场三条规则对决(id 完胜);下半场同权重对决(后写的赢)。把 #hero 规则删掉,看绿色 class 接管
比大小是逐位比较,不是加总分:10 个 class(0-10-0)也压不过 1 个 id(1-0-0)。实战心法:样式层级尽量用 class 表达;id 选择器留给锚点跳转;!important 只在调试时应急——到处 important 的代码,最终会变成谁都不敢动的雷区。

七、本章小结

  • 组合:空格选所有后代、> 只选亲儿子、+ 紧邻一个、~ 后面全部、逗号分组共用。
  • 属性[attr] 有就行、= 全等、^= 开头、$= 结尾、*= 包含、~= 整词(css3 ≠ css)。
  • 结构伪类管排行nth-child 系列混着数、of-type 系列分类型数;:first-child 必须真的是第一个孩子。
  • 状态伪类管心情:hover / active / focus / checked / disabled;:focus-within 让整个容器感知焦点。
  • 伪元素凭空造零件:::before / ::after 记得写 content;::first-letter 首字下沉;::selection 换选中色。
  • 打架看权重:数 (id, class/属性/伪类, 标签/伪元素) 三档逐位比;同权重后写的赢;!important 是核弹不是常规武器。
动手练习:做一个「部门通讯录」列表——nth-child(even) 打斑马纹,first-child 强调表头行;每个联系人写 data-dept="前端" 之类的属性,用属性选择器给不同部门染不同色;行 hover 高亮用 :hover;每行前面加一个部门色块——颜色本身用 ::before 画出来。全章武器一次用遍。