从零开始理解 CSS:让网页“活”起来的语言2

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的规则如下

  1. !important 声明会提升优先级(先比较 !important,再比较普通声明)。
  2. 同为普通声明时,内联样式(style 属性) 的特异性非常高,会覆盖绝大多数外部/内嵌选择器规则(除非外部/内嵌使用 !important)。
  3. 特异性数值示例(从高到低的"权重"理解):
    • 内联样式(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)
  4. 当特异性相同,后出现的规则覆盖先出现的(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 后代(任意层级)中的 B

    复制代码
    article p { margin-bottom: 1rem; }
  • 子选择器(>A > B:匹配 A 的直接 元素 B(这里谈到的子是直接的儿子,不包含孙子曾孙子等)

    复制代码
    ul > li { list-style: none; }
  • 相邻兄弟选择器(+A + B:匹配直接跟在 A 之后的兄弟 B

    复制代码
    h2 + p { margin-top: 0.25rem; }
  • 通用兄弟选择器(~A ~ B:匹配在同一父元素下、位于 A 之后的所有兄弟 B

    复制代码
    input: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)与覆盖规则

基本原则

按优先级顺序比较:

  1. Inline 样式(style="")最高(记作 1,0,0,0
  2. ID 选择器(#id)计入到第二位(比如 0,1,0,0
  3. 类/属性/伪类(.class, [attr], :hover)计入第三位(0,0,1,0
  4. 元素/伪元素(div, ::after)计入第四位(0,0,0,1
  5. !important 可以覆盖常规顺序(优先比较是否 !important,再比较特异性)

表示法:把特异性写成 (a,b,c,d) 四元组,逐位比较,先看 a,再看 b,以此类推。

Example: 数字逐位计算(按要求逐位算清楚)

选择器:#nav .item > a:hover::after

我们把它拆解计数:

  • #nav → ID:计为 b = 1
  • .item → 类:计为 c = 1
  • a → 元素:计为 d = 1
  • :hover → 伪类:也是类级别,计为 c += 1
  • ::after → 伪元素:元素级,计为 d += 1

逐位汇总(按顺序 a,b,c,d):

  • a(inline) = 0
  • b(ID) = 1
  • c(类/伪类/属性) = 1(.item) + 1 (:hover) = 2
  • d(元素/伪元素) = 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)。

提示:常见错误是把样式写在多个文件,然后忘了加载顺序,导致覆盖混乱。保持样式文件结构清晰,约定好全局与组件样式的加载顺序。

相关推荐
2501_918126913 分钟前
学习所有6502写游戏存档的语句
汇编·嵌入式硬件·学习·游戏·个人开发
橙露10 分钟前
Webpack/Vite 打包优化:打包体积减半、速度翻倍
前端·webpack·node.js
chushiyunen14 分钟前
python中的魔术方法(双下划线)
前端·javascript·python
sanshanjianke27 分钟前
AI辅助网文创作理论研究笔记(五):测试环境的搭建和一些问题的讨论
人工智能·笔记·ai写作
爱敲代码的菜菜29 分钟前
【测试】自动化测试
css·selenium·测试工具·junit·自动化·xpath
楠木68529 分钟前
从零实现一个 Vite 自动路由插件
前端
星雨流星天的笔记本30 分钟前
2.用洗洁精洗过的三口烧瓶的处理方法
学习
云边散步35 分钟前
godot2D游戏教程系列二(18)
笔记·学习·游戏
终端鹿40 分钟前
Vue2 迁移 Vue3 避坑指南
前端·javascript·vue.js
程序员陆业聪44 分钟前
工程师的瓶颈,已经不是代码了
前端