主题
CSS3 选择器与CSS3 新增盒子相关样式(Day12 2/3)
本页对应 Day12_CSS3,共 6 段视频,约 1 小时 30 分。
学习目标
- 能够使用
:enabled、:disabled、:checked为表单控件设置样式,并说明单选框、复选框在宽高、背景色等样式上的限制。 - 能够使用
:first-child、:last-child、:nth-child()、:nth-last-child()、:only-child选择符合兄弟位置条件的元素,并正确分析其与标签、类名组合后的选择结果。 - 能够区分
:nth-child()与:nth-of-type()的排号规则,并使用odd、even、2n、2n+1、3n等实现隔行换色和周期性样式。 - 能够使用
:not()排除指定选择器,完成如导航分割线等样式效果,并说明其作为附加条件的组合含义。 - 能够使用
::first-letter、::first-line、::before、::after、::placeholder、::selection设置样式,并说明::before/::after必须设置content及伪元素与伪类的区别。 - 能够比较标签、类、ID、行内样式和
!important的优先级,判断冲突样式的最终生效结果,并说明!important作用于单个属性而非选择器。
知识点
课前回顾:HTML5 与 CSS3 基础要点
本节开头先快速回顾上一阶段的知识框架,为后面的选择器学习做铺垫。
HTML5 新增标签方面,排版布局类标签包括 header、footer、nav、section、aside、article、main、figure、figcaption;状态类有 meter、progress;详情折叠用 details 与 summary;注音用 ruby 与 rt;文本高亮用 mark。这些标签让页面结构语义更清晰,浏览器和辅助设备能更好理解内容分区。
表单新增功能是重点回顾内容。表单控件新增属性有 placeholder、required、autofocus、autocomplete、form;新增控件类型包括 email、number、url、tel、search、range、color,以及日期时间类的 date、month、week、time、datetime-local;form 标签新增 novalidate 属性用于关闭原生校验;datalist 可以为搜索框提供下拉提示。这些能力让表单在不用 JavaScript 的情况下也能完成基础校验和交互。
音视频方面,video、audio 配合 source 标签可以指定多个格式来源,浏览器会自动选择可播放的那个。全局属性中,hidden 可以隐藏元素,a 标签新增的 download 属性则允许点击链接时下载资源而不是跳转。
CSS3 基本语法回顾了三点:浏览器私有前缀(如 -webkit-、-moz-)用于兼容旧版本浏览器;新增长度单位 rem、vw、vh、vmax、vmin 让响应式布局更灵活;新增颜色设置方式 rgba()、hsl()、hsla() 支持透明度和更直观的色相调节。
回顾完这些,我们进入本节的核心:用 CSS 选择器精准控制页面元素。
UI 元素伪类选择器:让表单状态可感知
为什么需要:表单控件有可用、禁用、选中三种状态,如果只靠 JavaScript 监听再改样式,代码量大且容易遗漏。CSS 的 UI 元素伪类选择器可以直接根据控件状态匹配样式。
怎么想:把状态当成“条件”。:enabled 匹配可用控件,:disabled 匹配禁用控件,:checked 匹配被选中的单选按钮、复选框或下拉选项。默认情况下控件都是可用的,所以 :enabled 会选中几乎所有控件。
怎么做:先准备一组表单控件,然后写:
css
input:enabled {
border: 1px solid #f00;
width: 100px;
height: 100px;
}
input:disabled {
background-color: #333;
}
input:checked {
width: 40px;
height: 40px;
}调试时会发现,复选框和单选框只能设置宽高,边框、背景色等很多样式不生效,这是浏览器原生控件的限制,不是选择器写错了。
易错点::checked 不区分“手动选中”还是“默认选中”,只要 checked 属性存在就会生效。另外,autofocus 会让输入框自动获得焦点并出现黑框,可能遮住自定义边框,调试时可以暂时去掉。
用 :checked 做选项卡:把单选按钮藏起来
为什么需要:选项卡通常要用 JavaScript 监听点击事件再切换内容。借助 :checked 和 label 的关联能力,可以纯 CSS 实现。
怎么想:三个单选按钮用相同 name 实现互斥;每个 label 通过 for 关联对应单选按钮;把单选按钮隐藏,点击 label 就相当于点击了单选按钮;再用 :checked + .item 让被选中按钮后面的内容块显示。
怎么做:
html
<div class="tab">
<input type="radio" name="tab" id="t1" checked>
<label for="t1">未发货订单</label>
<div class="item">未发货订单内容</div>
<input type="radio" name="tab" id="t2">
<label for="t2">未收货订单</label>
<div class="item">未收货订单内容</div>
</div>css
.tab .item { display: none; }
.tab input:checked + .item { display: block; }
.tab input { display: none; }易错点:+ 是相邻兄弟选择器,.item 必须紧跟在 input 后面,中间不能插入其他元素。隐藏 input 后不能再直接点击它,必须通过 label 的 for 关联来触发选中。
结构伪类选择器:把“第几个”当成条件
为什么需要:给列表隔行换色、给第一个或最后一个元素加特殊边框,如果手动加类名会很繁琐。结构伪类选择器可以按元素在兄弟中的位置匹配。
怎么想:关键是要把 :first-child 理解成一个独立条件——“你这个元素在所有兄弟中排第一”,而不是“第一个子元素”。判断兄弟关系只看位置,不看标签名。
怎么做:
css
li:first-child { border: 1px solid #f00; }
li:last-child { border: 1px solid #00f; }
li:nth-child(3) { background: #ccc; }
li:nth-child(odd) { background: #333; }
li:nth-child(2n) { background: #099; }
li:nth-child(3n) { border-bottom: 2px solid #f00; }易错点:li:first-child 表示“既是 li,又是兄弟中的第一个”,两个条件同时满足。如果写成 ul:first-child,则要求这个 ul 本身是兄弟中的第一个,而不是找 ul 的第一个子元素。很多教程在这里讲错,一定要按“条件一、条件二”逐个分析,不要组成“第一个某某元素”的句子。
:nth-of-type 与 :nth-child 的区别
为什么需要:当兄弟元素标签名不同时,:nth-child 是所有兄弟一起排号,:nth-of-type 是同标签名各自排号。
怎么想::nth-child(3) 要求在所有兄弟中排第三;:nth-of-type(3) 要求在同标签名兄弟中排第三。
怎么做:
css
.new p:nth-child(3) { color: red; } /* 可能选不到 */
.new p:nth-of-type(3) { color: red; } /* 同标签名中排第三的 p */易错点:如果结构里 p 前面还有 div,:nth-child(3) 可能选不到任何 p,因为那个位置的元素不是 p。分析时先看标签名条件,再看排号条件。
否定伪类与伪元素:排除与动态内容
为什么需要:有时想给“除了某个元素之外”的所有元素加样式,:not() 可以做到。伪元素则用于设置第一个字、第一行、前后插入内容等无法用普通元素选中的部分。
怎么做:
css
li:not(.active) { border-bottom: 1px solid #999; }
p::first-letter { font-size: 2em; color: #900; }
p::first-line { text-decoration: underline; }
.box::before { content: "前缀"; }
.box::after { content: "后缀"; }
input::placeholder { color: pink; }
::selection { background: pink; color: #009; }易错点:::before 和 ::after 必须写 content 属性,否则不生效;它们默认是行内元素,设置宽高需要改 display。伪元素不能像真实元素那样被 :first-letter 继续选中内部文字。:not() 括号里可以写标签名、类名、伪类等选择器。
选择器权重与 !important
为什么需要:多个选择器同时命中一个元素时,浏览器需要决定谁生效,这就是权重。
怎么想:行内样式权重高于 ID 选择器,ID 高于类选择器和伪类,类高于标签选择器。!important 加在某个属性后面,可以把这个属性的权重提到最高,甚至覆盖行内样式。
怎么做:
css
#box { color: red; }
.box { color: green !important; }易错点:!important 只针对单个属性,不是整个选择器。它必须写在分号前面。团队协作中要慎用,否则后期很难覆盖。如果非要覆盖,可以再加一个权重更高的 !important。
从表单控件认识 UI 元素伪类
为什么需要:表单控件有“可用/不可用/被选中”等状态,如果只用类名控制,就要写很多 JavaScript 或额外 class。UI 元素伪类让 CSS 直接按状态选元素。
怎么想::enabled 表示可用,:disabled 表示不可用,:checked 表示被选中。它们本质上都是“条件”,可以和标签名、类名组合。
怎么做:先准备多种表单控件,再写:
css
input:enabled {
border: 2px solid #f00;
width: 100px;
height: 100px;
}
input:disabled {
background-color: #333;
}
input:checked {
width: 40px;
height: 40px;
}调试时会发现:复选框、单选框的边框、背景色等样式常常不生效,宽高相对更明显。不要误以为选择器没选中,是控件本身限制。
易错点::enabled 是默认状态,不是“启用后才出现”;:disabled 要配合 disabled 属性;:checked 对默认选中项也生效。
用 :checked 做选项卡
为什么需要:选项卡传统上常用 JS 切换内容。利用 :checked、相邻兄弟选择器和 label 关联,可以纯 CSS 完成。
怎么想:三个 radio 负责记录状态,三个 label 负责被点击,三个内容块放在各自 input 后面。哪个 radio 被选中,就用 :checked + .tab-item 显示它后面的内容。
怎么做:
html
<div class="tabs">
<input type="radio" name="tab" id="t1" checked>
<label for="t1">未发货订单</label>
<div class="tab-item">未发货订单内容</div>
<input type="radio" name="tab" id="t2">
<label for="t2">未收货订单</label>
<div class="tab-item">未收货订单内容</div>
</div>css
.tabs input {
display: none;
}
.tab-item {
display: none;
border: 1px solid #ccc;
padding: 20px;
height: 100px;
}
.tabs input:checked + .tab-item {
display: block;
}调试过程:先不隐藏 input,点 label 看对应单选框是否选中;再验证 :checked + .tab-item 是否只显示对应内容;最后隐藏 input。隐藏后仍能通过 label for 触发选中。
易错点:+ 是紧邻的后一个兄弟,不是后代;label 的 for 必须对应 input 的 id;三个 radio 要有相同 name 才能单选。
结构伪类:先拆条件,不要“组句子”
为什么需要:要选第一个、最后一个、第 n 个、倒数第 n 个、独生子女等位置元素。
怎么想::first-child 不是“第一个子元素”,而是条件:该元素在所有兄弟中排第一。:last-child 是排最后;:nth-child(n) 是排第 n;:nth-last-child(n) 是倒数第 n;:only-child 是没有兄弟。组合选择器要按条件逐个分析。
怎么做:
css
li:first-child { border: 1px solid red; }
li:nth-child(3) { color: red; }
li:nth-last-child(4) { border: 2px solid red; }
li:only-child { background: #eee; }易错点:p:first-child 表示“既是 p,又是兄弟中第一个”,不是“第一个 p”。转写稿反复强调:不要试图组成通顺句子,要按“条件一、条件二”分析。nth-child 从 1 开始。
of-type 系列与高级 nth 用法
为什么需要:当兄弟中混有不同标签时,:nth-child 是所有兄弟一起排号,:nth-of-type 则是同标签名各排各的。
怎么想:div:first-of-type 是“在同类标签兄弟中排第一”;p:nth-of-type(3) 是“p 标签中排第三”。nth-child 与 nth-of-type 在全是同标签时结果常一样。
怎么做:隔行换色可用关键字或表达式:
css
li:nth-child(odd) { background: #333; }
li:nth-child(even) { background: #099; }
li:nth-child(2n) { background: #099; }
li:nth-child(2n + 1) { background: #333; }
li:nth-child(3n) { border-bottom: 2px solid #f00; }易错点:odd 是奇数,even 是偶数,不要凭字母猜;2n+1、2n-1 都表示奇数;3n 是 3、6、9……的倍数。
:not、:root、:empty
为什么需要:排除某些元素、选根元素、选空元素。
怎么做:
css
li:not(.active) { border-bottom: 2px solid #999; }
:root { color: #333; }
:empty { border: 1px solid red; }易错点::not() 里可以写选择器,含义是“不满足括号中的条件”;:root 在 HTML 中通常就是 html;:empty 要求标签之间既无文本也无后代,连空格都不能有。
伪元素:::first-letter、::before、::after 等
为什么需要:伪类选的是已有元素,伪元素则用于首字母、首行、前后追加内容、占位文字、选中文字等特殊位置。
怎么做:
css
p::first-letter { font-size: 2em; font-weight: bold; color: #900; }
p::first-line { text-decoration: underline; }
.box::before { content: "第一个子元素"; }
.box::after { content: "最后一个子元素"; }
input::placeholder { color: pink; }
::selection { background: pink; color: #009; }易错点:::before、::after 必须写 content,否则无效;它们默认是行内显示,常需 display:block 才能设宽高;::placeholder 主要改颜色,限制较多;::selection 设置鼠标选中文本样式。
选择器权重与 !important
为什么需要:多个样式命中同一元素时,要知道谁生效。
怎么想:行内样式通常高于 ID,高于类/伪类,高于标签。!important 不是给选择器加权,而是给某个属性“无限提高优先级”。
css
#box { color: red; }
.box { color: green; }
.box { color: green !important; }易错点:!important 必须写在属性值后、分号前;它只影响当前属性,不影响该选择器里的其他属性;合作开发中要慎用,否则后边很难覆盖。
说明:转写稿主要讲选择器,与本条“CSS3 新增盒子相关样式”不是同一主题,下面不把无关内容硬套进来;正文依据本节笔记与CSS规则扩写,保留原代码。
display:先理解“盒子怎样参与布局”
为什么需要:块级元素默认独占一行,行内元素默认不能随意设宽高。实际布局中,我们常需要让行内元素能设宽高,或让块级元素并排,因此要用 display 改变元素的显示类型。
怎么想:不要把 display 理解成“给元素设置任何样式”,它改变的是元素生成盒子的方式。比如 block 生成块级盒子,inline 生成行内盒子,inline-block 兼具行内排列和可设宽高,none 让元素不生成盒子。
怎么做:先确定元素要“占一行”还是“跟文字排”,再选择显示类型。需要设宽高又不想换行时,常用 inline-block;需要彻底隐藏且不占位时用 none。
易错点:display:none 不是透明,它会让元素不参与布局;行内元素直接设宽高通常无效,要先改显示类型。不要把 display 与 visibility 混用。
box-sizing:宽高到底算到哪一层
为什么需要:默认 width/height 只算内容区,加上 padding 和 border 后,盒子实际占位会变大,容易把布局撑破。
怎么想:先问“我写的宽高是内容宽高,还是整个盒子总宽高”。如果希望设置多少就占多少,用 border-box;如果希望宽高只约束内容区,用 content-box。
怎么做:笔记中的规则是:
content-box width和height设置的内容宽高,默认值
border-box width和height设置的总宽总高,也称为怪异模式实际写法是给元素设置 box-sizing: border-box;。这样 padding 和 border 会算进总宽高,内容区被相应压缩。
易错点:border-box 下总宽不变,但内容区会变小,文字可能更早换行;笔记中“怪异模式”是通俗叫法,本质是宽高包含 padding 和 border。调试时先加背景色和边框,观察实际占位。
box-shadow:阴影是画在盒子外面的“副本”
为什么需要:阴影能增加层次感,做卡片、按钮、浮层时很常用,而且不需要额外图片。
怎么想:先确定阴影往哪个方向偏,再决定模糊多大、是否外扩、是否放在盒内。语法顺序可记作:x偏移 y偏移 模糊值 外延值 颜色 内阴影,其中颜色和 inset 的位置可灵活些。
怎么做:保留笔记中的示例代码:
css
box-shadow: x偏移 y偏移 模糊值 外延值 颜色 内阴影;css
/* 设置阴影的偏移位置 x方向 y方向 */
box-shadow: 10px 5px;
/* 设置阴影的 偏移位置 颜色 */
box-shadow: -5px 10px #ccc;
box-shadow: #099 -5px -10px;
/* 设置阴影的模糊值 */
box-shadow: 5px 5px 10px;
/* 设置阴影的模糊值 颜色 */
box-shadow: 5px 5px 10px #ccc;
box-shadow: rgba(0,0,0,.3) 5px 5px 10px;
/* 阴影不偏移 靠模糊值向四周扩散 */
box-shadow: 0 0 15px #000;
/* 设置外延值 */
box-shadow: 3px 3px 5px 1px #080;
/* 设置内阴影 */
box-shadow: 10px 10px inset;
box-shadow: -10px 10px inset;
box-shadow: inset -10px -10px #000;
/* 多阴影 */
box-shadow: 5px 0 5px #f00,
0 5px 5px #080,
-5px 0 5px #088,
0 -5px 5px #880;调试时先只写两个偏移值,看到阴影方向;再加模糊值;最后加颜色或 inset。多阴影用逗号分隔,可叠加多层。
易错点:inset 是内阴影关键字,不是颜色;外延值可为负,会让阴影收缩;多阴影逗号不能漏。阴影不占布局空间,不会撑开盒子。
opacity:整体透明不是背景透明
为什么需要:做遮罩、禁用态、图片淡出时,需要让元素整体变淡。
怎么想:opacity 控制整个元素(包括内容、子元素)的透明程度;如果只想让背景半透明,应使用 rgba 背景色。
怎么做:取值 0 到 1,1 完全不透明,0 完全透明,数字越大越不透明。例如 opacity: .5;。
易错点:opacity 会影响子元素,不能只让背景透明;它不改变元素占位,透明元素仍参与布局。
粘连定位:滚动到阈值后“粘住”
为什么需要:做吸顶导航、表头固定时,希望元素在滚动到视口顶部时固定,而不是一上来就固定。
怎么想:sticky 是“滚动到某个阈值前像普通元素,到达阈值后像固定定位”。必须给它一个阈值,如 top:0。
怎么做:给元素设置 position: sticky; top: 0;。当页面滚动、元素顶部碰到视口顶部时粘住。
易错点:只写 position:sticky 不写 top/bottom/left/right 往往不生效;父级若有 overflow:hidden 等可能影响粘连;粘住范围受父容器限制,不会无限粘下去。调试时给元素加背景色,滚动页面观察它何时停住。
课堂画面






代码示例
示例一:UI 元素伪类选择器(:enabled / :disabled / :checked)+ :checked 选项卡
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>UI 元素伪类选择器</title>
<style>
body { font: 14px/1.5 "Microsoft YaHei", sans-serif; }
/* ===== 一、可用 / 不可用 / 被选中 ===== */
.form-box { width: 420px; margin: 20px auto; padding: 20px; border: 1px solid #ddd; }
/* 可用的表单控件(默认都是可用的) */
.form-box input:enabled { border: 1px solid #f00; }
/* 不可用的表单控件 */
.form-box input:disabled { background: #333; color: #fff; }
/* 被选中的单选框 / 复选框 / 下拉选项 */
.form-box input:checked { width: 40px; height: 40px; }
/* ===== 二、用 :checked 做选项卡 ===== */
.wrapper { width: 600px; margin: 40px auto; }
.tabbar label {
display: inline-block; /* label 默认是行内元素,改成行内块才能设宽高 */
padding: 0 20px;
line-height: 40px;
background: #ccc;
cursor: pointer;
}
/* 三个单选框全部隐藏,但依然能被 label 点中 */
.tab-content input { display: none; }
.tab-item {
display: none; /* 默认全部隐藏 */
box-sizing: border-box;
height: 100px;
padding: 20px;
border: 1px solid #ccc;
}
/* 核心:被选中的单选框紧邻的那个 tab-item 显示出来 */
.tab-content input:checked + .tab-item { display: block; }
</style>
</head>
<body>
<div class="form-box">
<p><input type="text" placeholder="可用文本框"></p>
<p><input type="text" placeholder="不可用文本框" disabled></p>
<p>
<label><input type="radio" name="sex" checked>男</label>
<label><input type="radio" name="sex">女</label>
</p>
<p>
<label><input type="checkbox" checked>篮球</label>
<label><input type="checkbox">足球</label>
</p>
<p><input type="submit" value="提交"></p>
</div>
<div class="wrapper">
<div class="tabbar">
<label for="t01">未发货订单</label>
<label for="t02">未收货订单</label>
<label for="t03">未评价订单</label>
</div>
<div class="tab-content">
<input type="radio" name="tab" id="t01" checked>
<div class="tab-item">这里是未发货的订单</div>
<input type="radio" name="tab" id="t02">
<div class="tab-item">这里是未收货的订单</div>
<input type="radio" name="tab" id="t03">
<div class="tab-item">这里是未评价的订单</div>
</div>
</div>
</body>
</html>要点解释
:enabled、:disabled、:checked都表示一个条件,权重等同于类选择器,所以写在后面可以覆盖前面的样式。- 单选框和复选框能设置的样式非常有限:宽高可以,边框、背景色多数浏览器不生效,这是最常见的“为什么我设了没效果”的坑。
- 选项卡的三个关键点:
- 三个
input用相同的name才能互斥单选; input:checked + .tab-item用的是相邻兄弟选择器+,所以input和.tab-item必须紧挨着写,中间不能插入别的元素;input被display: none隐藏后仍然可以被<label for="id">点击触发选中,这是整个案例能成立的前提。
- 三个
label的for值必须和input的id一一对应,id不能重复。
示例二:结构伪类选择器 + 否定伪类选择器
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>结构伪类 / 否定伪类选择器</title>
<style>
body { font: 14px/1.5 "Microsoft YaHei", sans-serif; }
ul.news { width: 800px; margin: 20px auto; padding: 0; list-style: none; }
ul.news li { padding: 10px 20px; }
/* 隔行换色:奇数行 / 偶数行(关键字写法) */
ul.news li:nth-child(odd) { background: #333; color: #fff; }
ul.news li:nth-child(even) { background: #099; color: #fff; }
/* 3 的倍数行:3、6、9 ……(表达式写法) */
ul.news li:nth-child(3n) { border-left: 6px solid #f00; }
/* 否定伪类:除了最后一个,其余都加一条下边框 */
ul.news li:not(:last-child) { border-bottom: 1px dashed #999; }
/* ===== 混合结构:div 和 p 都是 .new-item ===== */
.new { width: 600px; margin: 20px auto; }
.new-item { padding: 10px; margin-bottom: 10px; border: 1px solid #ccc; }
/* 所有兄弟中的第一个 */
.new-item:first-child { border-color: #f00; }
/* 同标签名兄弟中的第一个 */
.new p:first-of-type { background: #ffc; }
/* 同标签名兄弟中排第 3 个 */
.new-item:nth-of-type(3) { font-weight: bold; }
</style>
</head>
<body>
<ul class="news">
<li>新闻列表项 1</li>
<li>新闻列表项 2</li>
<li>新闻列表项 3</li>
<li>新闻列表项 4</li>
<li>新闻列表项 5</li>
<li>新闻列表项 6</li>
<li>新闻列表项 7</li>
<li>新闻列表项 8</li>
<li>新闻列表项 9</li>
</ul>
<div class="new">
<div class="new-item">我是 div,排行第一</div>
<p class="new-item">我是 p,排行第二</p>
<div class="new-item">我是 div,排行第三</div>
<p class="new-item">我是 p,排行第四</p>
<div class="new-item">我是 div,排行第五</div>
</div>
</body>
</html>要点解释
:nth-child(n)里的n是所有兄弟元素一起排的号,odd= 奇数行、even= 偶数行,也可以写2n、2n+1、3n这样的数学表达式,n从 1 开始数。:first-child不是“找第一个子元素”,而是条件:你这个元素在所有兄弟中排第一。所以.new-item:first-child是两个条件取交集——类名是new-item且 排第一。:nth-child和:nth-of-type的区别是本节的最大易错点:nth-child是所有兄弟一起排号,nth-of-type是同标签名各排各的号。所以示例里.new-item:nth-of-type(3)命中的是“第 5 个元素(div 中的第 3 个)”,而不是第 3 个元素。:not(选择器)里可以写类名、标签名、伪类等任意选择器;示例中li:not(:last-child)表示“是 li 且不是最后一个”,比li + li的写法更灵活。- 分析这类选择器时不要试图把它读成一句通顺的话,要按“条件一、条件二”逐条判断,否则很容易理解成“第一个 P”“第三个 li”而掉坑。
示例三:伪元素选择器 + 选择器权重
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>伪元素选择器</title>
<style>
body { font: 14px/1.5 "Microsoft YaHei", sans-serif; }
.item {
width: 600px;
margin: 20px auto;
padding: 20px;
background: #ccc;
}
/* 元素中的第一个字 */
.item::first-letter { font-size: 2em; font-weight: bold; color: #900; }
/* 元素中的第一行文字 */
.item::first-line { text-decoration: underline; }
/* 在元素内容最前面动态添加一个子元素 */
.item::before { content: "【开始】"; color: #f00; }
/* 在元素内容最后面动态添加一个子元素 */
.item::after { content: "【结束】"; color: #090; }
/* 输入框占位符文字 */
.box input::placeholder { color: #e91e63; }
/* 鼠标选中的文字 */
::selection { background: #ffc0cb; color: #009; }
/* ===== 权重对比:ID > 类 ===== */
#main { color: #f00; }
.item { color: #090; }
/* 若在某个属性后加 !important,该属性的权重将高于行内样式 */
</style>
</head>
<body>
<div class="item" id="main">
前端开发中,HTML 负责结构,CSS 负责样式,JavaScript 负责行为。伪元素选择器可以给元素中的第一个字、第一行文字设置样式,还能用 before 和 after 动态添加内容。
</div>
<div class="item">
这一段文字用来对比 ::first-letter 与 ::first-line 的效果,注意 first-letter 只作用于第一个字。
</div>
<p class="box"><input type="text" placeholder="请输入用户名"></p>
</body>
</html>要点解释
- 伪元素统一写双冒号。
::first-letter、::first-line、::before、::after在旧标准里用单冒号也能生效,但::placeholder、::selection是 CSS3 新增的,必须双冒号。 ::before/::after必须写content属性,否则整条规则无效;即使内容为空也要写content: ""。它们默认是行内元素,想设宽高或让它撑满一行,需要加display: block。::first-letter只对块级元素生效,且只作用于第一个字符(英文、中文都可以)。::placeholder能改的样式很有限,基本就是颜色这类,别指望改字体大小位置。::selection作用于被鼠标选中的文字,写作::selection(无 s)。- 伪元素不是 HTML 结构中真实存在的元素,所以
::first-letter不会跑到::after生成的内容里去取第一个字。 - 权重规则:行内样式 > ID 选择器 > 类 / 伪类 / 属性选择器 > 标签选择器。示例中第一个
div有id="main",所以颜色按#main的红色显示,.item的绿色被覆盖。 !important提升的是某一个属性的权重(必须写在分号前),不是整个选择器的权重,且要慎用——一旦加上,别人只能也加!important并保证权重更高才能覆盖。
常见错误
- 把
li:first-child读成“第一个 li” → 把伪类当成标签限定,实际是“是 li 且是兄弟中第一个”两个条件 → 分析时拆成条件逐条判断,混合标签场景改用:first-of-type验证。 :nth-child(3)选不到预期的p→ 与:nth-of-type(3)的排号范围混淆 → 全体兄弟统一排号用child,同标签分别排号用of-type,先构造混合结构测试。- 用
:checked做选项卡时点击文字无反应 →label的for与input的id不匹配,或隐藏方式导致不可点 → 检查关联关系,隐藏input但保留label可点,内容用相邻兄弟选择器显示。 ::before/::after不显示 → 漏写content,或默认inline下设置宽高无效 → 补content:"";,需要宽高/定位时先设display:block或inline-block。::placeholder设置字号、浮动、布局等无效 → 该伪元素可设置属性有限 → 主要改颜色等受支持样式,布局交给输入框本身。:not()结果不符合预期 → 把not当成简单“排除标签名”,忽略括号内是完整选择器条件 → 括号内写完整选择器,并与前置选择器按交集条件分析。!important后样式仍覆盖失败或失控 → 误以为它提升整个选择器权重,实际只提升单条声明 → 少用;覆盖时优先提高选择器权重或调整顺序,双方都加时再比权重与顺序。
练习
- 用三个
radio + label + 内容块实现选项卡:点击 label 切换内容、默认选中一项、隐藏 radio,并写出完整选择器链。 - 给 10 个
li做奇数灰、偶数蓝,再给每 3 项加下边框;插入一个非li兄弟后,对比:nth-child(3n)与:nth-of-type(3n)的结果。 - 给导航
a用:not(:first-child)加左分割线;再改成:not(:last-child)加右边框,要求不增加额外类名。 - 用
::before/::after给卡片做上下装饰线,content为空也要写;分别测试默认inline与改为block后的宽高表现。 - 设置输入框和文本域的
::placeholder颜色、::selection选中文本颜色;制造一个!important覆盖冲突,再用提高权重或调整顺序修复并记录最终生效规则。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | UI元素伪类选择器 | 18:45 |
| 2 | 结构伪类选择器 | 31:24 |
| 3 | 结构伪类选择器高级应用 | 07:41 |
| 4 | 否定伪类选择器 | 05:29 |
| 5 | 伪元素选择器 | 21:20 |
| 6 | 选择器权重 | 05:46 |
小结
- 伪类、伪元素都按“条件”逐个分析,不要强行翻译成一句自然语言。
child系列是全体兄弟统一排号,of-type系列是同标签分别排号;混合结构先确认排号范围。NTH系列适合做奇偶、倍数、周期样式,优先用odd/even/2n/3n等表达。:checked方案的关键链条是 label 关联、radio 选中、相邻兄弟显示内容。::before/::after必须写content,默认inline,需要布局能力时先改显示模式。:not()括号内写完整选择器,结果与前置选择器按交集理解。!important只作用于单条声明,不提升选择器权重;覆盖优先靠权重和顺序。- 新代码统一使用双冒号伪元素写法,旧单冒号仅作兼容;生成内容不要当真实 DOM 子元素处理。