术语
在 CSS 里,最基本的单元是一行代码,我们将其称作声明(Declaration)。它由两部分构成:属性(Property,例如color)和值(Value,例如red),共同描绘出样式的具体呈现效果。就如同给元素披上一件 “红色外衣”:
color: red;
当多条声明汇聚在一起,并被大括号{}包裹时,就形成了一个声明块(Declaration Block)。而位于声明块前面的部分,比如body,我们把它叫做选择器(Selector),它的作用是确定这些样式将应用到哪些元素上。例如:
body {
color: red; /* 文字变红 */
background-color: blue; /* 背景变蓝 */
}
选择器与声明块相结合,构成了一个规则集(Rule Set),简称为规则。它就像是一份指令清单,指导浏览器如何对页面上的元素进行渲染。
层叠
CSS 的精妙之处在于,实现同一个效果往往存在多种途径,比如设置文字颜色,可以借助类选择器,也能使用行内样式。但追根溯源,CSS 的核心在于定义规则 —— 在特定条件下,让元素展现出我们期望的样式。单条规则理解起来并不困难,然而,当多条规则同时作用于一个元素,却未达到预期效果时,很可能是它们之间产生了冲突。要解开这个困惑,就必须深入理解层叠(Cascade)机制。
层叠是 CSS 解决声明冲突的方法。当多条规则都试图控制同一个属性时,浏览器会依据以下三个标准来判定最终生效的规则:
- 样式表的来源:样式源自何处?是浏览器默认样式,还是我们自行编写的样式?
- 选择器优先级:哪个选择器的 “权重” 更高,更具权威性?
- 源代码顺序:若前两者难分高下,那么后出现的规则将占据主导。
举例来说,假设<p>标签同时受到浏览器默认样式(color: black)和我们编写的样式(color: blue)的影响,最终显示为蓝色,这是因为层叠机制优先考虑了我们编写的规则。
样式表的来源
我们为网页添加的样式并非浏览器唯一参照的 “设计蓝图”。除了我们编写的作者样式表(Author Stylesheet)之外,还存在一种隐藏的样式来源 —— 用户代理样式表(User Agent Stylesheet),也就是浏览器自带的默认样式。例如,<h1>默认呈现为大号粗体字,<a>默认显示为蓝色且带有下划线,这些都是用户代理样式表的作用。
在实际开发过程中,我们经常会重置这些默认样式,比如使用margin: 0来覆盖浏览器默认的外边距。倘若你长期从事 CSS 开发工作,或许早已习惯了这种 “覆盖” 操作,而这恰恰是层叠的来源规则在发挥效用。作者样式表的优先级高于用户代理样式表,所以你编写的color: blue能够轻松战胜浏览器的color: black。
理解优先级
当样式来源无法确定最终生效的规则时,浏览器会转向第二个考量因素 —— 优先级(Specificity)。它会仔细比较冲突规则的 “重要程度”,以此来决定究竟该采用哪条规则。优先级主要分为以下两类:
HTML 行内样式
如果你在 HTML 元素中直接使用style属性,例如:
<h1 style="color: red">你好</h1>
这被称为行内样式(Inline Style),它的优先级极高,几乎处于无敌状态。它能够覆盖任何来自外部样式表或<style>标签内的规则。若要在样式表中挑战它的权威,唯一的手段是使用!important,例如color: blue !important。不过,倘若行内样式也添加了!important,那么它依旧会稳坐 “优先级王座”。
选择器优先级
对于非行内样式而言,优先级由选择器决定,具体规则如下:
- ID 胜出:选择器中 ID 数量较多的一方获胜,例如
#header(1 个 ID)胜过.nav(0 个 ID)。 - 类次之:当 ID 数量相同时,比较类的数量,
.nav.active(2 个类)胜过.nav(1 个类)。 - 标签垫底:若类的数量也相同,则比较标签数量,
div p(2 个标签)胜过p(1 个标签)。
#main .title {
color: blue;
} /* 1个ID + 1个类 */
.title span {
color: green;
} /* 1个类 + 1个标签 */
注意:伪类(如:hover)和属性选择器(如[type="text"])的权重等同于一个类。通用选择器(*)和组合器(>、+等)不增加权重
在上述例子中,#main .title的优先级更高,文字最终显示为蓝色。
小建议:在实际应用中,应尽量避免使用 ID 和!important,因为它们的权重过高,在后续需要覆盖样式时会带来诸多不便。保持较低的优先级,能够为后续的样式调整留出更多的灵活性空间。
源码顺序
如果样式来源和优先级都无法确定最终的样式规则,层叠机制会采用最后一个策略 —— 源码顺序(Source Order)。简单来讲,就是 “后来者居上”。后出现的声明将覆盖前面的声明。例如:
p {
color: blue;
}
p {
color: red;
}
在上述代码中,段落文字最终会显示为红色,因为color: red这一声明出现得更晚。如果涉及多个样式表,那么后引入的样式表中的规则将生效:
<link rel="stylesheet" href="style1.css" /> /* p { color: blue; } */
<link rel="stylesheet" href="style2.css" /> /* p { color: red; } */
最终结果依然是文字显示为红色,因为style2.css是后加载的。
链接样式
源码顺序在链接样式的设置中尤为关键,因为它决定了链接在多种状态下的样式呈现。假设一个<a>标签同时具备 “访问过” 和 “悬停” 两种状态,那么后定义的样式将覆盖前面的样式。例如:
a:visited {
color: purple;
}
a:hover {
color: red;
}
当鼠标悬停在链接上时,链接文字会显示为红色,这是因为:hover状态的样式定义在后面。如果再对链接进行点击操作:
a:hover {
color: red;
}
a:active {
color: green;
}
在点击链接的瞬间,链接文字会变为绿色,因为:active状态的样式定义更为靠后。
为了确保链接样式的正确显示,有一个书写顺序口诀可供参考:LVHA(爱恨交织),即:
:link(未访问链接):visited(已访问链接):hover(悬停状态):active(激活状态)
层叠值
层叠值(Cascaded Value)是层叠过程最终确定应用到元素上的某个属性的值。浏览器按照来源、优先级、源码顺序这三个步骤来确定最终的样式规则。如果某个属性没有被任何规则明确指定,它将不会有直接的层叠值,但可能会从父元素继承相应的值(这一点将在后续的继承部分详细阐述)。如果多条规则同时针对同一个属性进行设置,例如:
p {
color: blue;
}
.text {
color: red;
}
对于<p class="text">这样的元素,层叠机制会选择red作为color属性的层叠值。一个元素在同一时刻只能有一个生效的属性值,就如同元素的高度和宽度不能同时拥有两个不同的值一样,最终只有在层叠过程中胜出的那个值才会被采用。
继承
除了层叠机制外,CSS 还存在另一种样式来源 —— 继承(Inheritance)。如果一个元素的某个属性没有自身的层叠值,它会向上查找,并继承最近祖先元素的对应值。例如,我们常常会为body元素设置font-family:
body {
font-family: Arial;
}
此时,body内部的<p>、<div>等元素会自动采用 Arial 字体。需要注意的是,并非所有属性都可以继承,像color、font-size等属性可以继承,而border、margin等属性则不能继承。通常情况下,能够继承的属性(大多与文本相关)正是我们期望子元素与父元素保持一致的部分。
继承路径有时可能难以追踪,尤其是在属性值被覆盖的情况下。幸运的是,浏览器的开发者工具可以为我们提供帮助。打开样式检查器,你将看到元素的每个选择器(按照优先级进行排列),在其下方还有一个 “继承” 面板,该面板会列出属性值的继承来源,使整个继承关系一目了然。
特殊值
CSS 提供了两个特殊的值,用于对层叠和继承进行控制:
- inherit:该值用于强制元素继承父元素的值。例如:
div {
color: inherit;
} /* 子div使用父元素的颜色 */
通过使用inherit,可以覆盖其他规则,强制元素遵循父元素的样式设置。
2. initial:将属性值重置为浏览器的默认值,类似于执行 “恢复出厂设置” 操作。例如:
p {
color: initial;
} /* 恢复为默认的black */
需要注意的是,默认值并不一定意味着 “没有设置任何值”,例如display属性的initial值为inline。
简写属性
简写属性(Shorthand Property)是一种能够一次性设置多个属性的便捷方式。例如,border属性就是border-width、border-style和border-color的组合简写形式:
border: 1px solid red;
这种简写方式简洁高效,但也存在一些潜在的问题。以font属性为例,它包含了font-size、font-family等众多属性,如果在设置font属性时遗漏了font-weight:
h1 {
font: 16px Arial;
} /* font-weight被设置为normal */
那么原本可能设置为粗体的字体将变为普通字体。由于font属性包含的属性值较多,因此更容易出现类似的问题。所以,建议仅在设置通用样式(如为body元素设置全局字体)时使用简写属性。
简写顺序
对于border属性,border: 1px solid red与border: red solid 1px的效果是相同的,因为浏览器能够识别宽度、样式和颜色各自对应的属性类型。然而,对于像margin、padding这类涉及元素四边的属性,其值的设置顺序是按照逆时针方向,从元素的上边界开始(上右下左):
margin: 10px 20px 30px 40px; /* 上10px,右20px,下30px,左40px */
如果只设置三个值,例如10px 20px 30px,那么其效果等同于10px 20px 30px 20px,即左边界的值与右边界的值相同(取对边的值)。如果只设置两个值,例如10px 20px,则表示上下边界的值为10px,左右边界的值为20px(分别对应水平和垂直方向)。
相对单位
绝对单位回顾
CSS 支持绝对单位,像像素(px)(96px≈1 英寸)、pt(1/72 英寸),固定不变。但高清屏下,px 不等于物理像素,需缩放。
相对单位的崛起
相对单位随环境变,适合响应式:
- 好处:适配性强(4 到 30 英寸)、维护简单(改一行全局变)、用户友好(缩放不崩)。
常见单位:
- em:基于父元素字号:
div {
font-size: 16px;
}
p {
font-size: 1.5em;
} /* 24px */
嵌套累积,难预测。
- rem:基于根元素
<html>字号:
html {
font-size: 16px;
}
p {
font-size: 1.5rem;
} /* 24px */
全局一致,好控制。
- %:基于父元素属性:
div {
width: 200px;
}
p {
width: 50%;
} /* 100px */
布局利器。
为啥用相对单位?
像素简单但死板,14px 小屏太小、大屏挤。相对单位让样式动态调整,比如用 rem 缩放全站。
盒模型
盒模型(Box Model) 是 CSS 布局的基石,每个元素都被看作一个“盒子”。它决定了元素在页面上的大小、间距和位置。初学者理解盒模型,就像拆解一个包裹:里面是东西(内容),外面有包装(内边距、边框、外边距)。它包括四部分:
- 内容(Content):由 width 和 height 定义,元素的核心区域,比如文字或图片。
- 内边距(Padding):内容到边框的缓冲区,让内容“喘口气”。
- 边框(Border):围住内容的框,像包裹的硬壳。
- 外边距(Margin):盒子外的空间,跟其他盒子保持距离。
div {
width: 100px;
height: 50px;
padding: 10px;
border: 2px solid black;
margin: 20px;
}
- 内容:宽 100px,高 50px。
- 内边距:每边 10px(上下左右)。
- 边框:每边 2px。
- 外边距:每边 20px。
总尺寸怎么算?
- 宽:内容 100 + 左内边距 10 + 右内边距 10 + 左边框 2 + 右边框 2 = 124px。
- 高:内容 50 + 上内边距 10 + 下内边距 10 + 上边框 2 + 下边框 2 = 74px。
- 外边距:不算进总尺寸,只影响周围元素,像是“社交距离”。
浏览器默认用 box-sizing: content-box,只算内容宽高,内边距和边框往外加。
box-sizing 两种模式
盒模型有两种计算方式,靠 box-sizing 控制:
- content-box(默认):
- 只算 width 和 height,内边距和边框往外扩。
- 上例中,设 width: 100px,实际占 124px。
- 坑:新手常忘加内边距和边框,导致布局超预期。
- border-box:
- width 和 height 包含内边距和边框,总大小固定。
- 改上例为:
div {
width: 100px;
height: 50px;
padding: 10px;
border: 2px solid black;
box-sizing: border-box;
}
- 总宽:100px,内容宽压缩到 100 – 10×2 – 2×2 = 76px。
- 总高:50px,内容高压缩到 50 – 10×2 – 2×2 = 26px。
- 好处:设多少就是多少,不用手动算,开发神器。
全局设置:很多开发者开项目就写:
{
box-sizing: border-box;
}
这样所有元素都用 border-box,布局更直观。
常见问题与解决
- 溢出:内容太大撑破盒子?
- 用 overflow: hidden 剪掉多余部分,或 overflow: auto 加滚动条。
div {
width: 100px;
height: 50px;
overflow: hidden;
}
外边距塌陷:上下 margin 重叠,只取大的。
- 比如两个 div,上一个 margin-bottom: 20px,下一个 margin-top: 30px,间距是 30px,不是 50px。
- 解决:加父容器,或用 padding 代替。
实用技巧
负 margin:可以用负值拉近盒子,甚至重叠,调整布局很灵活。
- 比如两个 div 默认间隔 20px,想紧贴:
.div1 {
margin-bottom: 20px;
}
.div2 {
margin-top: -20px;
} /* 抵消间隔,紧贴div1 */
- 效果:间距变成 0,div2 上移贴住 div1。
- 高级用法:负值还能做偏移,比如图片超出容器:
img {
width: 100px;
margin-left: -20px; /* 左移20px,超出容器 */
}
- 注意:负值别滥用,布局复杂时可能乱套,建议先测好效果。
小结
CSS 通过术语定义样式规则,用层叠机制处理冲突,借助继承传递属性,以相对单位实现屏幕适配,并依靠盒模型构建布局基础。这些核心概念串联起从固定设计到响应式设计的演变,构成了 CSS 的基石,为初学者理解和掌握样式表打下扎实根基。