CSS笔记小节
前言
之前的CSS笔记整理的有些草率,这里重新仔细的整理一部分笔记,重新理解一下CSS的三种文本存在格式:行内(inline)、内联(internal/embedded)、外部(external)。也顺便把选择器梳理一下。
重新看行内(inline)、内联(internal/embedded)、外部(external)
首先,搞清楚定义:
- 行内样式(inline style) :指直接写在 HTML 元素上的
style属性,例如<div style="color:red">。 - 内联样式 / 嵌入式样式(internal / embedded) :指放在 HTML 文档
<head>中的<style>...</style>块内的样式,作用于该 HTML 文档。 - 外部样式表(external stylesheet) :指放在单独
.css文件并通过<link rel="stylesheet" href="...">引入的样式。
在代码中,我们直接的体现如下所示
行内样式(inline)
html
<!-- 直接在元素上写 style -->
<button style="background:#007bff; color:white; padding:8px 12px;">
提交
</button>
内联/嵌入式样式(internal)
html
<head>
<style>
.btn { background: #007bff; color: white; padding: 8px 12px; }
@media (max-width: 600px) { .btn { padding: 10px; } }
</style>
</head>
<body>
<button class="btn">提交</button>
</body>
外部样式表(external)
html
<!-- index.html -->
<head>
<link rel="stylesheet" href="/styles/main.css">
</head>
<body>
<button class="btn">提交</button>
</body>
/* /styles/main.css */
.btn {
background: #007bff;
color: white;
padding: 8px 12px;
}
优先级的处理问题
CSS的灵活很容易导致一个严肃的问题。大型工程中,如果出现了实际上重复样式渲染的选择,我们如何裁决。这里,CSS的规则如下
!important声明会提升优先级(先比较!important,再比较普通声明)。- 同为普通声明时,内联样式(
style属性) 的特异性非常高,会覆盖绝大多数外部/内嵌选择器规则(除非外部/内嵌使用!important)。 - 特异性数值示例(从高到低的"权重"理解):
- 内联样式(style attribute) → 记作最高(常表述为 1,0,0,0)
- ID 选择器
#id→ 比如 (0,1,0,0) - 类/属性/伪类
.class,[attr],:hover→ (0,0,1,0) - 元素/伪元素
div,p,::after→ (0,0,0,1)
- 当特异性相同,后出现的规则覆盖先出现的(CSS 文件加载顺序和位置决定「后来者获胜」)。
html
<div id="box" class="green" style="color: red;">文本</div>
- 若
.green { color: green; }在外部样式中定义,最终文本是 红色 (因为内联style覆盖)。 - 如果外部写了
.green { color: green !important; },则 绿色 会胜出(author!important与 author 普通规则比较时!important胜出)。 - 如果内联也写了
style="color:red !important",那内联的!important胜。
三种样式的比较和推介使用
很显然,有三种样式存在的方式,我们就可以在不同的场景下进行灵活选择。
行内样式(inline)
行内样式(inline)的好处就是立刻生效、简洁,适合单次小调整或由 JS 动态设置样式 (element.style)。对于临时调试、或邮件模板中(很多邮件客户端不支持外部样式)非常有用。但是现代大型工程显然不推介这种方式,因为它破坏结构与样式分离(HTML 混入样式)规则,难维护、重复代码多;特异性很高,容易引发覆盖困难("特异性战争")。除了这种维护困难,性能上也会出现大问题:无法被浏览器单独缓存(每个页面都会重复);最致命的也许是与 CSP(Content Security Policy)冲突:许多站点禁止 style-src 'unsafe-inline',所以行内可能被阻止。所以笔者注意到,除非有很正当的理由,否则不要用。
内联/嵌入式样式(<style>)
内联/嵌入式样式便于将某个页面的样式集中在同一文件中,适合单页或独立页面的局部样式。但是他也有麻烦,比如说不同页面之间不能被缓存重用(如果多个页面都内嵌同样样式,会重复传输),而且,好像还是没有分离的很开,仍然会给我们的开发造成大量的麻烦,所以这种方式只有在单页应用或页面级样式、调试、快速原型、或构建时将 critical CSS 放到 <head> 中以优化首屏渲染的时候使用
外部样式表(.css 文件 + <link>)⭐⭐⭐⭐⭐
这才是大家使用的最多的方式,外部样式表可以让我们的渲染样式集中、可重用、便于维护、可以被浏览器缓存,减少重复传输;此外易于与构建工具和版本控制配合
CSP(Content Security Policy)与内联样式
很多站点出于安全会设置 CSP,例如:
http
Content-Security-Policy: style-src 'self';
这会拒绝任何行内样式或未授权的内联 <style>。
选择器:选择器的概念与分类(为什么选择器重要)
很经典的问题:你怎么让浏览器知道你要渲染谁,对哪一个元素进行渲染?选择器就是告诉浏览器------你要渲染哪一批元素。他就是CSS 的"定位"机制:告诉浏览器把哪些样式应用到文档中哪些元素上。
- 简单选择器(类型/类/ID/通配符)
- 属性选择器(基于元素属性)
- 组合器(描述元素之间的关系)
- 伪类 / 伪元素(状态与虚拟元素)
- 选择器分组(用逗号合并多个选择器)
选择器写得好,样式更可读、可维护、且能避免不必要的渲染开销。
基本(简单)选择器详解
元素选择器(type selector)
这个选择器是直接Target到了指定的元素了,比如说p标签,比如说div标签等等。
p { line-height: 1.6; }
nav { display: flex; }
所以这种标签主要用的是针对全局的设置。
类选择器(class selector)
最常用、推荐的选择方式。以 . 开头,匹配 class 属性包含该类名的元素。
.btn { padding: 8px 12px; }
.card.header { /* 同时包含 .card 和 .header */ }
低特异性、复用性高、和组件化配合良好(BEM 等)。
ID 选择器(id selector)
以 # 开头,匹配 id 属性相同的单个元素。
#main { max-width: 1200px; margin: 0 auto; }
注意:ID 的特异性高,且通常页面中只应出现一次。现代团队常建议用类替代 ID 做样式(ID 多用于 JS 钩子或锚点)。
通配选择器(universal selector)
*,匹配所有元素(注意性能与广度)。
* { box-sizing: border-box; }
注意的是,这种选择器往往在开发中被禁用掉(或者是拿来做清除某一些效果用的)
属性选择器(attribute selectors)
属性选择器通过元素属性筛选元素,形式灵活,常用于无类库或第三方组件的定制。
[attr]:存在某属性
input[required] { border-left: 3px solid #f00; }[attr="value"]:完全匹配
a[target="_blank"] { /* 新窗口链接 */ }[attr~="value"]:空格分隔的词列表中匹配一个词(常用于 class 备份场景)
*[class~="btn"] { /* 等同于 .btn */ }[attr|="value"]:以 value 或 value- 开头(常用于语言)
html[lang|="en"] { }[attr^="value"]:以 value 开头(prefix)
a[href^="mailto:"] { /* 邮件链接 */ }[attr$="value"]:以 value 结尾(suffix)
img[src$=".svg"] { }[attr*="value"]:包含 value(substring)
a[href*="example.com"] { }
属性选择器对动态属性和第三方生成的 DOM 很有用,但表达式复杂时要注意性能与可读性。
组合器(combinators)与关系选择器
描述元素之间的父子/兄弟/后代关系。
-
后代选择器(space) :
A B:匹配位于A后代(任意层级)中的Barticle p { margin-bottom: 1rem; } -
子选择器(
>) :A > B:匹配A的直接子 元素B(这里谈到的子是直接的儿子,不包含孙子曾孙子等)ul > li { list-style: none; } -
相邻兄弟选择器(
+) :A + B:匹配直接跟在A之后的兄弟Bh2 + p { margin-top: 0.25rem; } -
通用兄弟选择器(
~) :A ~ B:匹配在同一父元素下、位于A之后的所有兄弟Binput:checked ~ .details { display: block; }
选择器越具体、越嵌套越深,维护成本越高。优先使用类并限制层级深度(例如不超过 3 层复杂链)。
伪类(pseudo-classes)与伪元素(pseudo-elements)
常见伪类(表示元素状态)
-
:hover、:active、:focus、:visited、:checked、:disabled、:nth-child()、:nth-of-type()、:not()等。button:hover { transform: translateY(-2px); }
li:nth-child(odd) { background: #f8f8f8; }
a:visited { color: purple; }
:not() 是选择器否定伪类;注意它本身不增加特异性,但它参数的特异性会被计入
常见伪元素(创建虚拟子元素)
-
::before、::after、::first-line、::first-letter等(现代语法使用双冒号::,单冒号兼容旧浏览器)。.btn::before { content: "★ "; }
p::first-letter { font-size: 2em; float: left; }
伪类用于"状态"、伪元素用于"生成内容或样式化文档的一部分"。两类常搭配使用(例如 a:hover::after { ... })。
选择器优先级(specificity)与覆盖规则
基本原则
按优先级顺序比较:
- Inline 样式(
style="")最高(记作1,0,0,0) - ID 选择器(
#id)计入到第二位(比如0,1,0,0) - 类/属性/伪类(
.class,[attr],:hover)计入第三位(0,0,1,0) - 元素/伪元素(
div,::after)计入第四位(0,0,0,1) !important可以覆盖常规顺序(优先比较是否!important,再比较特异性)
表示法:把特异性写成
(a,b,c,d)四元组,逐位比较,先看 a,再看 b,以此类推。
Example: 数字逐位计算(按要求逐位算清楚)
选择器:
#nav .item > a:hover::after我们把它拆解计数:
#nav→ ID:计为b = 1.item→ 类:计为c = 1a→ 元素:计为d = 1:hover→ 伪类:也是类级别,计为c += 1::after→ 伪元素:元素级,计为d += 1逐位汇总(按顺序 a,b,c,d):
a(inline) = 0b(ID) = 1c(类/伪类/属性) = 1(.item) + 1 (:hover) = 2d(元素/伪元素) = 1 (a) + 1 (::after) = 2最终特异性 =
(0, 1, 2, 2)。任何具有更高b(ID)或在b相同下更高c的规则会覆盖本规则;若所有位相同,则文档中后出现的规则胜出(later wins)。
7 --- 选择器分组、层叠规则与顺序
-
分组选择器(用逗号):复用样式,等价于多条独立规则,但写起来更短。
h1, h2, h3 { font-family: "Noto Sans", sans-serif; }
-
层叠(Cascading)规则 :当多个规则匹配同一元素,按照先比较
!important,再比较来源(user/author/UA),再比较特异性,最后比较出现顺序(later wins)。
提示:常见错误是把样式写在多个文件,然后忘了加载顺序,导致覆盖混乱。保持样式文件结构清晰,约定好全局与组件样式的加载顺序。